This is an automated email from the git hooks/post-receive script. New commit to annotated tag v1.0.0 in repository i18n. See https://gitlab.nuiton.org/nuiton/i18n.git commit e34d86e46baca4a13c40384c91b80035bba348d0 Author: Tony Chemit <chemit@codelutin.com> Date: Sat Aug 22 23:35:30 2009 +0000 fi xurl in tags... --- LICENSE.txt | 166 ++++ README.txt | 3 + changelog.txt | 2 + maven-i18n-plugin/LICENSE.txt | 166 ++++ maven-i18n-plugin/README.txt | 2 + maven-i18n-plugin/changelog.txt | 44 + maven-i18n-plugin/pom.xml | 125 +++ .../org/nuiton/i18n/plugin/AbstractI18nPlugin.java | 282 +++++++ .../main/java/org/nuiton/i18n/plugin/Bundle.java | 361 ++++++++ .../main/java/org/nuiton/i18n/plugin/Generate.java | 144 ++++ .../main/java/org/nuiton/i18n/plugin/Getter.java | 115 +++ .../java/org/nuiton/i18n/plugin/I18nArtifact.java | 100 +++ .../java/org/nuiton/i18n/plugin/I18nLogger.java | 117 +++ .../i18n/plugin/parser/AbstractI18nParser.java | 369 ++++++++ .../java/org/nuiton/i18n/plugin/parser/Parser.java | 55 ++ .../org/nuiton/i18n/plugin/parser/ParserEvent.java | 59 ++ .../nuiton/i18n/plugin/parser/ParserException.java | 45 + .../i18n/plugin/parser/event/KeysModifier.java | 219 +++++ .../nuiton/i18n/plugin/parser/impl/ParserJava.java | 164 ++++ .../plugin/parser/impl/ParserJavaActionConfig.java | 87 ++ .../plugin/parser/impl/ParserJavaTabConfig.java | 35 + .../nuiton/i18n/plugin/parser/impl/ParserJaxx.java | 113 +++ .../i18n/plugin/parser/impl/ParserSwixat.java | 104 +++ .../i18n/plugin/parser/impl/ParserValidation.java | 194 +++++ .../nuiton/i18n/plugin/parser/impl/ParserXml.java | 255 ++++++ .../services/org.apache.commons.logging.LogFactory | 1 + maven-i18n-plugin/src/main/resources/jaxx.rules | 37 + .../src/main/resources/log4j.properties | 10 + maven-i18n-plugin/src/main/resources/swixat.rules | 33 + .../src/main/resources/validation.rules | 3 + .../src/main/resources/xwork-validator-1.0.2.dtd | 42 + maven-i18n-plugin/src/site/apt/index.apt | 42 + maven-i18n-plugin/src/site/apt/usages.apt | 8 + maven-i18n-plugin/src/site/site.xml | 46 + nuiton-i18n-api/LICENSE.txt | 166 ++++ nuiton-i18n-api/README.txt | 2 + nuiton-i18n-api/changelog.txt | 2 + nuiton-i18n-api/doc/FormatConverter-uml.uxf | 34 + nuiton-i18n-api/pom.xml | 102 +++ .../src/main/java/org/nuiton/i18n/CountryEnum.java | 284 +++++++ .../org/nuiton/i18n/I18nDefaultTooltipFilter.java | 43 + .../main/java/org/nuiton/i18n/I18nFileReader.java | 131 +++ .../src/main/java/org/nuiton/i18n/I18nFilter.java | 37 + .../src/main/java/org/nuiton/i18n/I18nUtil.java | 194 +++++ .../main/java/org/nuiton/i18n/LanguageEnum.java | 181 ++++ .../java/org/nuiton/i18n/bundle/I18nBundle.java | 160 ++++ .../org/nuiton/i18n/bundle/I18nBundleEntry.java | 192 +++++ .../org/nuiton/i18n/bundle/I18nBundleFactory.java | 613 ++++++++++++++ .../org/nuiton/i18n/bundle/I18nBundleScope.java | 144 ++++ .../main/java/org/nuiton/util/LocaleConverter.java | 126 +++ .../org.apache.commons.beanutils.Converter | 1 + nuiton-i18n-api/src/site/apt/index.apt | 20 + nuiton-i18n-api/src/site/site.xml | 33 + .../nuiton/i18n/bundle/I18nBunsleScopeTest.java | 63 ++ .../java/org/nuiton/util/LocaleConverterTest.java | 124 +++ .../src/test/resources/log4j.properties | 11 + nuiton-i18n-editor/LICENSE.txt | 674 +++++++++++++++ nuiton-i18n-editor/README.txt | 2 + nuiton-i18n-editor/changelog.txt | 8 + nuiton-i18n-editor/pom.xml | 384 +++++++++ nuiton-i18n-editor/src/main/assembly/bin.xml | 53 ++ nuiton-i18n-editor/src/main/assembly/go.bat | 1 + nuiton-i18n-editor/src/main/assembly/go.sh | 4 + .../src/main/filters/nuiton-i18n-editor.properties | 78 ++ .../java/org/nuiton/i18n/editor/I18nEditor.java | 201 +++++ .../org/nuiton/i18n/editor/I18nEditorConfig.java | 340 ++++++++ .../org/nuiton/i18n/editor/I18nEditorContext.java | 343 ++++++++ .../i18n/editor/project/AbstractI18nProject.java | 425 ++++++++++ .../project/AbstractI18nProjectProvider.java | 107 +++ .../nuiton/i18n/editor/project/I18nProject.java | 198 +++++ .../project/I18nProjectConfigurePanelUI.java | 40 + .../i18n/editor/project/I18nProjectFactory.java | 177 ++++ .../i18n/editor/project/I18nProjectProvider.java | 120 +++ .../editor/project/impl/DirectoryI18nProject.java | 67 ++ .../impl/DirectoryI18nProjectConfigurePanelUI.jaxx | 94 +++ .../project/impl/DirectoryI18nProjectProvider.java | 124 +++ .../i18n/editor/project/impl/JarI18nProject.java | 86 ++ .../impl/JarI18nProjectConfigurePanelUI.jaxx | 106 +++ .../project/impl/JarI18nProjectProvider.java | 186 ++++ .../i18n/editor/ui/BundleCheckBoxMenuUI.jaxx | 56 ++ .../org/nuiton/i18n/editor/ui/BundleValueUI.css | 52 ++ .../org/nuiton/i18n/editor/ui/BundleValueUI.jaxx | 87 ++ .../org/nuiton/i18n/editor/ui/BundleValuesUI.jaxx | 54 ++ .../org/nuiton/i18n/editor/ui/CreateBundleUI.css | 43 + .../org/nuiton/i18n/editor/ui/CreateBundleUI.jaxx | 80 ++ .../org/nuiton/i18n/editor/ui/CreatePackageUI.css | 40 + .../org/nuiton/i18n/editor/ui/CreatePackageUI.jaxx | 72 ++ .../org/nuiton/i18n/editor/ui/I18nEditorUI.css | 260 ++++++ .../org/nuiton/i18n/editor/ui/I18nEditorUI.jaxx | 343 ++++++++ .../nuiton/i18n/editor/ui/I18nEditorUIHandler.java | 937 +++++++++++++++++++++ .../i18n/editor/ui/PackageCheckBoxMenuUI.jaxx | 66 ++ .../org/nuiton/i18n/editor/ui/PropertieNode.java | 122 +++ .../nuiton/i18n/editor/ui/PropertiesTreeModel.java | 199 +++++ .../org/nuiton/i18n/editor/ui/TreeModelMode.java | 36 + .../nuiton/i18n/editor/ui/project/ProjectStep.java | 67 ++ .../nuiton/i18n/editor/ui/project/ProjectUI.css | 59 ++ .../nuiton/i18n/editor/ui/project/ProjectUI.jaxx | 299 +++++++ .../i18n/editor/ui/project/ProjectUIModel.java | 301 +++++++ .../editor/ui/project/SelectBundlesTableModel.java | 196 +++++ .../ui/project/tabs/AbstractProjectTabPanelUI.css | 41 + .../ui/project/tabs/AbstractProjectTabPanelUI.jaxx | 88 ++ .../ui/project/tabs/ChooseProjectTypePanelUI.jaxx | 72 ++ .../ui/project/tabs/ConfigureProjectPanelUI.jaxx | 70 ++ .../editor/ui/project/tabs/PersistPanelUI.jaxx | 174 ++++ .../i18n/editor/ui/project/tabs/ResumePanelUI.jaxx | 57 ++ .../ui/project/tabs/SelectBundlesPanelUI.jaxx | 57 ++ nuiton-i18n-editor/src/main/jnlp/jxlayer.jnlp | 12 + nuiton-i18n-editor/src/main/jnlp/sun.jnlp | 12 + ....nuiton.i18n.editor.project.I18nProjectProvider | 2 + .../i18n/nuiton-i18n-editor-en_GB.properties | 120 +++ .../i18n/nuiton-i18n-editor-fr_FR.properties | 120 +++ .../src/main/resources/icons/action-about.png | Bin 0 -> 936 bytes .../src/main/resources/icons/action-accept.png | Bin 0 -> 781 bytes .../src/main/resources/icons/action-add.png | Bin 0 -> 733 bytes .../src/main/resources/icons/action-calculator.png | Bin 0 -> 543 bytes .../src/main/resources/icons/action-cancel.png | Bin 0 -> 587 bytes .../src/main/resources/icons/action-close.png | Bin 0 -> 688 bytes .../src/main/resources/icons/action-closeTab.png | Bin 0 -> 518 bytes .../main/resources/icons/action-collapseAll.png | Bin 0 -> 372 bytes .../main/resources/icons/action-combobox-reset.png | Bin 0 -> 396 bytes .../main/resources/icons/action-combobox-sort.png | Bin 0 -> 574 bytes .../src/main/resources/icons/action-config.png | Bin 0 -> 611 bytes .../resources/icons/action-connect_creating.png | Bin 0 -> 1099 bytes .../src/main/resources/icons/action-connect_no.png | Bin 0 -> 601 bytes .../src/main/resources/icons/action-connect_ok.png | Bin 0 -> 619 bytes .../resources/icons/action-connect_untested.png | Bin 0 -> 698 bytes .../src/main/resources/icons/action-connected.png | Bin 0 -> 748 bytes .../src/main/resources/icons/action-db-change.png | Bin 0 -> 763 bytes .../src/main/resources/icons/action-db-local.png | Bin 0 -> 806 bytes .../src/main/resources/icons/action-db-none.png | Bin 0 -> 659 bytes .../src/main/resources/icons/action-db-remote.png | Bin 0 -> 755 bytes .../src/main/resources/icons/action-delete.png | Bin 0 -> 783 bytes .../src/main/resources/icons/action-edit.png | Bin 0 -> 574 bytes .../src/main/resources/icons/action-exit.png | Bin 0 -> 830 bytes .../src/main/resources/icons/action-expandAll.png | Bin 0 -> 371 bytes .../main/resources/icons/action-fileChooser.png | Bin 0 -> 441 bytes .../src/main/resources/icons/action-fullscreen.png | Bin 0 -> 687 bytes .../src/main/resources/icons/action-go-back.png | Bin 0 -> 345 bytes .../src/main/resources/icons/action-go-detail.png | Bin 0 -> 576 bytes .../src/main/resources/icons/action-go-down.png | Bin 0 -> 379 bytes .../src/main/resources/icons/action-go-jump.png | Bin 0 -> 723 bytes .../src/main/resources/icons/action-go-up.png | Bin 0 -> 372 bytes .../src/main/resources/icons/action-help.png | Bin 0 -> 746 bytes .../src/main/resources/icons/action-i18n-es.png | Bin 0 -> 469 bytes .../src/main/resources/icons/action-i18n-fr.png | Bin 0 -> 545 bytes .../src/main/resources/icons/action-i18n-gb.png | Bin 0 -> 599 bytes .../src/main/resources/icons/action-import-gps.png | Bin 0 -> 923 bytes .../main/resources/icons/action-information.png | Bin 0 -> 778 bytes .../resources/icons/action-leave-fullscreen.png | Bin 0 -> 727 bytes .../main/resources/icons/action-local-export.png | Bin 0 -> 528 bytes .../main/resources/icons/action-local-import.png | Bin 0 -> 532 bytes .../main/resources/icons/action-mode-create.png | Bin 0 -> 714 bytes .../src/main/resources/icons/action-mode-read.png | Bin 0 -> 779 bytes .../main/resources/icons/action-mode-update.png | Bin 0 -> 813 bytes .../src/main/resources/icons/action-next-step.png | Bin 0 -> 676 bytes .../src/main/resources/icons/action-open.png | Bin 0 -> 693 bytes .../main/resources/icons/action-previous-step.png | Bin 0 -> 655 bytes .../main/resources/icons/action-remote-export.png | Bin 0 -> 755 bytes .../main/resources/icons/action-remote-import.png | Bin 0 -> 770 bytes .../src/main/resources/icons/action-revert.png | Bin 0 -> 780 bytes .../src/main/resources/icons/action-save.png | Bin 0 -> 838 bytes .../resources/icons/action-select-ssl-cert.png | Bin 0 -> 693 bytes .../src/main/resources/icons/action-show-help.png | Bin 0 -> 744 bytes .../src/main/resources/icons/action-site.png | Bin 0 -> 928 bytes .../src/main/resources/icons/action-synch.png | Bin 0 -> 912 bytes .../main/resources/icons/action-synchro-pause.png | Bin 0 -> 598 bytes .../main/resources/icons/action-synchro-start.png | Bin 0 -> 592 bytes .../main/resources/icons/action-synchro-stop.png | Bin 0 -> 403 bytes .../src/main/resources/icons/action-translate.png | Bin 0 -> 790 bytes .../main/resources/icons/action-unconnected.png | Bin 0 -> 796 bytes .../src/main/resources/icons/action-validate.png | Bin 0 -> 537 bytes .../src/main/resources/icons/logo OT_rvb.png | Bin 0 -> 143337 bytes .../src/main/resources/icons/logo-OT_web.png | Bin 0 -> 34309 bytes .../src/main/resources/icons/logo_ird.png | Bin 0 -> 12384 bytes .../src/main/resources/log4j.properties | 14 + nuiton-i18n-editor/src/site/apt/index.apt | 25 + nuiton-i18n-editor/src/site/site.xml | 51 ++ pom.xml | 147 ++++ src/site/site.xml | 36 + 179 files changed, 14496 insertions(+) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3f7b8b1 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d046c31 --- /dev/null +++ b/README.txt @@ -0,0 +1,3 @@ +To deploy new version of pom: mvn clean deploy -DperformRelease +To install localy: mvn clean install + diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..a35341f --- /dev/null +++ b/changelog.txt @@ -0,0 +1,2 @@ +1.0.0 xxx xxx + * Initial release (merge of previous lutin projects) \ No newline at end of file diff --git a/maven-i18n-plugin/LICENSE.txt b/maven-i18n-plugin/LICENSE.txt new file mode 100644 index 0000000..3f7b8b1 --- /dev/null +++ b/maven-i18n-plugin/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/maven-i18n-plugin/README.txt b/maven-i18n-plugin/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/maven-i18n-plugin/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/maven-i18n-plugin/changelog.txt b/maven-i18n-plugin/changelog.txt new file mode 100644 index 0000000..6156fa8 --- /dev/null +++ b/maven-i18n-plugin/changelog.txt @@ -0,0 +1,44 @@ +1.0.0 ? + * migrate to groupId org.nuiton + +0.12 chemit 20090511 + * bump versions (lutinproject, lutinprocessor, maven-license-switcher-plugin) + * improve download section on site + * update site + * introduce bundle goal to merge all bundle into one for final application + * no more use of bundles property, prefer locales one (with real Locale object instead of simple String one...) + +0.11 chemit 20090311 + * 20090420 [chemit] - bump versions + * 20090417 [chemit] - replace bundles parameters from tyep String[] to String type to make easier configuration + - use lutinproject 3.5.xx + - use lutinpluginutil 0.4 + +0.10 chemit 20090311 + * 20090223 [chemit] will skip get and gen goal if no getters were registred while parsing goals +0.9 chemit 20090218 + * 20090217 [chemit] use project.build.sourceEncoding instead of maven.compile.encoding as default encoding + * 20090214 [chemit] add safeMode, showTouchedFiles properties in parser mojo (none safeMode will improve performance) + * 20090205 [chemit] use lutinproject 3.4 +0.8 chemit 20090107 + * 20081205 [chemit] modify ParserValidation (message can by suffix by ## to delimite args) + +0.7 chemit 20081117 + * 20081205 [chemit] use lutinpluginproject 3.2 +ver-0-7 chemit 20081117 + * 20081118 [chemit] use lutinpluginproject 3.1 + * 20081026 [chemit] add xworks validator parse + * 20081026 [chemit] improve parser mojo logging + +ver-0-6 thimel 20080922 + * 20080925 [chemit] Using lutinpluginutil 0.2 and license-switcher in pom (no more in superpom) + * 20080922 [thimel] Using lutinpluginproject 3.0 + +ver-0-5 chemit 20080824 + * 20080824 [chemit] refactoring code + unificiation des reader et writer pour que cela fonctionne en utf8 + * 20080824 [chemit] suppresion dependance lutinutil + * 20080824 [chemit] passage du projet en utf8 + * 20080824 [chemit] passage en maven 2 layout directory + +ver-0-4 + * [chemit] pas maintenu avant ! diff --git a/maven-i18n-plugin/pom.xml b/maven-i18n-plugin/pom.xml new file mode 100644 index 0000000..b3e19f6 --- /dev/null +++ b/maven-i18n-plugin/pom.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + <parent> + <groupId>org.nuiton</groupId> + <artifactId>i18n</artifactId> + <version>1.0.0</version> + </parent> + + <groupId>org.nuiton.i18n</groupId> + <artifactId>maven-i18n-plugin</artifactId> + + <dependencies> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>nuiton-i18n-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.nuiton.processor</groupId> + <artifactId>nuiton-processor</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>maven-helper-plugin</artifactId> + <type>maven-plugin</type> + </dependency> + + <dependency> + <groupId>xalan</groupId> + <artifactId>xalan</artifactId> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <!-- tests dependencies --> + + <!--dependency> + <groupId>org.nuiton</groupId> + <artifactId>maven-helper-plugin</artifactId> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency--> + + <!-- provided dependencies --> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + </dependency> + + <!-- FIXME si on ne le rajoute pas, on se retrouve avec la version 1.1 qui ne convient pas --> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + </dependency> + + </dependencies> + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + <name>nuiton-i18n - maven plugin</name> + <description> + Plugin pour maven 2 de génération des bundles pour l'internationnalisation (i18n) des applications java + basé sur nuiton-i18n-api. + </description> + <inceptionYear>2007</inceptionYear> + + <!-- ************************************************************* --> + <!-- *** Build Settings ****************************************** --> + <!-- ************************************************************* --> + + <packaging>maven-plugin</packaging> + + <build> + <defaultGoal>install</defaultGoal> + + <plugins> + + <!-- plugin plugin --> + <plugin> + <artifactId>maven-plugin-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>helpmojo</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + + </build> + + <reporting> + <plugins> + <plugin> + <artifactId>maven-plugin-plugin</artifactId> + </plugin> + </plugins> + </reporting> + +</project> diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nPlugin.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nPlugin.java new file mode 100644 index 0000000..fa7e63a --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/AbstractI18nPlugin.java @@ -0,0 +1,282 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin; + +import org.apache.maven.plugin.AbstractMojo; +import org.nuiton.i18n.plugin.parser.ParserEvent; +import org.nuiton.i18n.I18nUtil; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.SortedSet; +import org.nuiton.util.PluginHelper; + +/** + * Classe permettant d'obenir les parametres pendant les différentes phases + * du plugin. + * + * @author julien + */ +public abstract class AbstractI18nPlugin extends AbstractMojo { + + /** + * Le nombre de getters détectés pendant le cycle de vie du build. + */ + private static int NB_GETTER_FILES = 0; + /** + * Répertoire de stockage des fichiers i18n pour la recuperation des fichiers + * de traduction entre librairie + */ + protected static final String DIRECTORY_INSTALL = "i18n" + File.separatorChar; + /** + * Nom du projet. + * + * @parameter expression="${i18n.artifactId}" default-value="${project.artifactId}" + * @readonly + */ + protected String artifactId; + /** + * Langues des bundles generes. + * <p/> + * + * @parameter expression="${i18n.bundles}" default-value="fr_FR,en_GB" + * @required + */ + protected String bundles; + /** + * Repertoire sources des fichiers i18n. + * + * @parameter expression="${i18n.src}" default-value="${basedir}/src/main/resources/i18n" + * @required + */ + protected File src; + /** + * Repertoire des fichiers generes i18n. + * + * @parameter expression="${i18n.out}" default-value="${basedir}/target/generated-sources/i18n" + * @required + */ + protected File out; + /** + * encoding a utiliser pour charger et sauver les bundles + * + * @parameter expression="${i18n.encoding}" default-value="${project.build.sourceEncoding}" + * @required + */ + protected String encoding; + /** + * Met les fichiers generes dans le repertoire des sources i18n. + * <p/> + * Note: Par défaut active, pour pouvoir paquager avec les bundles mis a jour. + * + * @parameter expression="${i18n.genSrc}" default-value="true" + */ + protected boolean genSrc; + /** + * Active la modification de cle. + * <p/> + * Note: par defaut, on ne l'active pas (build sur serveur non ui). + * + * @parameter expression="${i18n.keysModifier}" default-value="false" + */ + protected boolean keysModifier; + /** + * verbose flag + * <p/> + * Note: si non renseigne, on utilise la propiété <code>maven.verbose</code>. + * + * @parameter expression="${i18n.verbose}" default-value="${maven.verbose}" + */ + protected boolean verbose; + /** + * silent flag + * + * @parameter expression="${i18n.silent}" default-value="false" + * @since 1.0.0-rc-5 + */ + protected boolean silent; + /** + * conserve les anciens fichiers de traduction avec un suffix ~ + * <p/> + * Note: par defaut, on ne l'active pas. + * + * @parameter expression="${i18n.keepBackup}" default-value="false" + */ + protected boolean keepBackup; + /** + * ne conserve que les clef scannees (et donc traite tous les fichiers) + * + * <p/> + * Note : par default, on ne l'active car rescanne tous les fichiers. + *s + * @parameter expression="${i18n.strictMode}" default-value="false" + */ + protected boolean strictMode; + /** Liste des évènements */ + protected List<ParserEvent> events = new ArrayList<ParserEvent>(); + protected Locale[] locales; + /**logger verbeux */ + protected I18nLogger verboseLog; + + /** + * Ajoute un évènement + * + * @param parserEvent l'évènement d'ajout + */ + protected void addParserEvent(ParserEvent parserEvent) { + this.events.add(parserEvent); + } + + /** + * Supprime un évènement + * + * @param parserEvent l'évènement de suppression + */ + protected void removeParserEvent(ParserEvent parserEvent) { + this.events.remove(parserEvent); + } + + public void init() { + + verboseLog = new I18nLogger(this); + + if (verbose) { + getLog().info("config - verbose mode is on"); + } + locales = I18nUtil.parseLocales(bundles); + } + + public String getArtifactId() { + return artifactId; + } + + /** + * + * @return <code>true</code> si des getters ont etes enregistres pendant + * le cycle de vie, <code>false</code> sinon. + */ + protected boolean needGeneration() { + boolean needGeneration = NB_GETTER_FILES > 0; + return needGeneration; + } + + /** + * Prend en compte qu'un getter a été détecté. + * + * Cela veut dire qu'un goal de parser a détecté des clefs. Il + * faudra donc activer les goal get et gen. + */ + protected void addGetter() { + NB_GETTER_FILES++; + } + + protected I18nLogger getVerboseLog() { + return verboseLog; + } + + /** + * @param root le repertoire ou sont stockes les fichiers i18n + * @param artifactId le nom de l'artifact + * @param locale le nom du bundle + * @param create <code>true</code> pour creer le fichier si non present + * @return le fichier i18n + * @throws java.io.IOException si probleme lors de la creation du fichier + */ + public File getI18nFile(File root, String artifactId, Locale locale, boolean create) throws IOException { + File file = new File(root.getAbsolutePath() + File.separatorChar + artifactId + "-" + locale.toString() + ".properties"); + if (create && !file.exists()) { + if (!file.exists()) { + file.getParentFile().mkdirs(); + } + file.createNewFile(); + } + return file; + } + + /** + * @param root le repertoire ou sont stockes les fichiers getter + * @param getter le nom du getter + * @param create <code>true</code> pour creer le fichier si non present + * @return le fichier i18n + * @throws java.io.IOException si probleme lors de la creation du fichier + */ + public File getGetterFile(File root, String getter, boolean create) throws IOException { + File file = new File(root.getAbsolutePath() + File.separatorChar + getter); + if (create && !file.exists()) { + if (!file.exists()) { + file.getParentFile().mkdirs(); + } + file.createNewFile(); + } + return file; + } + + /** + * @param root le repertoire ou sont stockes les fichiers getter + * @param getter le nom du getter + * @return le fichier i18n + */ + public File getGetterFileBackup(File root, String getter) { + return new File(root.getAbsolutePath() + File.separatorChar + getter + '~'); + } + + /** + * @param root le reertoire ou sont stockes les fichiers i18n + * @param artifactId le nom de l'artifact + * @param bundle le nom du bundle + * @return le fichier i18n de backup + */ + public File getI18nFileBackup(File root, String artifactId, Locale bundle) { + return new File(root.getAbsolutePath() + File.separatorChar + artifactId + "-" + bundle.toString() + ".properties~"); + } + + protected void checkBundle(Locale locale, Properties propertiesOut, boolean showEmpty) { + // on verifie qu'il n'y a pas de traduction vide + SortedSet<String> emptyEntries = PluginHelper.getEmptyKeys(propertiesOut); + if (!emptyEntries.isEmpty()) { + StringBuilder buffer = new StringBuilder(); + int size = emptyEntries.size(); + buffer.append("bundle " + locale + " contains " + size + "/" + propertiesOut.size() + " empty entries!"); + if (showEmpty) { + int index = 0; + for (String key : emptyEntries) { + buffer.append("\n - " + (index++) + "/" + size + " : " + key); + } + } else { + buffer.append(" (use -Di18n.showEmpty to see these entries)"); + } + getLog().warn(buffer.toString()); + } else { + if (!silent && verbose) { + getLog().info("bundle " + locale + " is valid (no empty entries)."); + } + } + } + + protected void backupFile(File f) throws IOException { + PluginHelper.copy(f, new File(f.getAbsolutePath() + "~")); + } + + protected void copyFile(File src, File dst) throws IOException { + PluginHelper.copy(src, dst); + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Bundle.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Bundle.java new file mode 100644 index 0000000..18e32ef --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Bundle.java @@ -0,0 +1,361 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin; + +import java.net.MalformedURLException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactCollector; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.shared.dependency.tree.DependencyNode; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; +import org.nuiton.i18n.bundle.I18nBundleEntry; +import org.nuiton.util.PluginHelper.SortedProperties; +import org.nuiton.i18n.bundle.I18nBundleFactory; +import org.nuiton.util.DependencyUtil; +import org.nuiton.util.PluginHelper; + +/** + * Créer un bundle pour une application finale. + * + * Cela génère un merge de tous les fichiers i18n utilisés en un seul. + * + * On utilise la dépendance sur les artifacts pour connaitre l'ordre le chargement + * des bundles. + * + * Si dans un bundle childs, la valeur de la clef est vide, on conserve alors celui + * du parent, + * + * Ainsi on obtient un bundle dont toutes les clefs sont traduites. + * + * Le but aussi d'utiliser un unique bundle est de gagner du temps au runtime + * car la recherche des bundles devient trop couteuse en temps lorsque l'on a de + * nombreuses dépendances (au dessus de 100 deps cela peut prendre plusieurs + * secondes, ce qui 'est pas acceptable). + * + * On a ajoute un second mode d'initialisation dans la clesse I18n pour n'utiliser + * qu'un seul bundle et courcircuiter le chargement couteux... + * * + * + * @author chemit + * @goal bundle + * @phase generate-resources + * @execute goal=gen + * @requiresProject true + * @requiresDependencyResolution runtime + * + * @since 0.12 + */ +public class Bundle extends AbstractI18nPlugin { + + /** + * Repertoire ou generer les bundles. + * + * @parameter expression="${i18n.bundleOutputDir}" default-value="${basedir}/target/generated-sources/resources/META-INF" + * @required + * @since 1.0.0 + */ + protected File bundleOutputDir; + /** + * Nom du bundle a generer. + * + * @parameter expression="${i18n.bundleOutputName}" default-value="${project.artifactId}-i18n" + * @required + * @since 1.0.0 + */ + protected String bundleOutputName; + /** + * Un drapeau pour vérifier que les bundles ne contiennent pas d'entrées vides. + * + * @parameter expression="${i18n.checkBundle}" default-value="true" + * @required + * @since 1.0.0 + */ + protected boolean checkBundle; + /** + * Un drapeau pour afficher les entrées vides. (nécessite {@link #checkBundle} activé). + * + * @parameter expression="${i18n.showEmpty}" default-value="false" + * @required + * @since 1.0.0 + */ + protected boolean showEmpty; + /** + * Dependance du projet. + * + * @parameter default-value="${project}" + * @required + * @since 1.0.0 + */ + protected MavenProject project; + /** + * Local Repository. + * + * @parameter expression="${localRepository}" + * @required + * @readonly + * @since 1.0.0 + */ + protected ArtifactRepository localRepository; + /** + * Remote repositories used for the project. + * + * @parameter expression="${project.remoteArtifactRepositories}" + * @required + * @readonly + * @since 1.0.0 + */ + protected List<?> remoteRepositories; + /** + * Dependency tree builder component. + * + * @component + */ + protected DependencyTreeBuilder dependencyTreeBuilder; + /** + * Artifact Factory component. + * + * @component + */ + protected ArtifactFactory factory; + /** + * Artifact metadata source component. + * + * @component + */ + protected ArtifactMetadataSource artifactMetadataSource; + /** + * Artifact collector component. + * + * @component + */ + protected ArtifactCollector collector; + /** + * Maven Project Builder component. + * + * @component + */ + protected MavenProjectBuilder mavenProjectBuilder; + protected I18nArtifact[] i18nArtifacts; + protected ClassLoader loader; + protected URL[] urls; + + @Override + public void init() { + super.init(); + + if (locales == null || locales.length == 0) { + throw new IllegalStateException("il faut au moins une locale declaree (utiliser la propriete 'bundles')"); + } + + if (!bundleOutputDir.exists()) { + bundleOutputDir.mkdirs(); + } + + try { + // calcul des artifacts qui ont un bundle i18n et trie selon les + // dependances + + i18nArtifacts = detectI18nArtifacts(); + + if (!silent) { + getLog().info("detected " + i18nArtifacts.length + " i18n artifact(s) : "); + for (I18nArtifact a : i18nArtifacts) { + getLog().info(" - " + a); + } + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + // ajout de repertoire de generation (le parent en fait) + // dans les resources du projet + + String newresourceDir = bundleOutputDir.getParentFile().getAbsolutePath(); + + PluginHelper.addResourceDir(newresourceDir, project); + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + if ("pom".equals(project.getPackaging()) || "site".equals(project.getPackaging())) { + return; + } + + + long t00 = System.nanoTime(); + + init(); + + if (!silent) { + getLog().info("config - bundle name : " + bundleOutputName); + getLog().info("config - baseidr : " + bundleOutputDir); + getLog().info("config - locales : " + Arrays.toString(locales)); + } + // la locale par defaut est la première + Locale defaultLocale = locales[0]; + + Map<Locale, String> bundleDico = new LinkedHashMap<Locale, String>(locales.length); + + try { + + for (Locale locale : locales) { + + long t0 = System.nanoTime(); + + File bundleOut = getI18nFile(bundleOutputDir, bundleOutputName, locale, false); + + if (!silent) { + getLog().info("generate bundle for locale " + locale); + } + + SortedProperties propertiesOut = new SortedProperties(encoding, false); + StringBuilder buffer = new StringBuilder(); + for (I18nArtifact artifact : i18nArtifacts) { + I18nBundleEntry[] bundleEntries = artifact.getBundleEntries(locale, defaultLocale); + for (I18nBundleEntry bundleEntry : bundleEntries) { + + bundleEntry.load(propertiesOut); + String strPath = bundleEntry.getPath().toString(); + int index = strPath.indexOf("i18n/"); + + buffer.append(',').append(strPath.substring(index)); + if (verbose) { + getLog().info("loaded " + bundleEntry.getPath() + " in " + PluginHelper.convertTime(t0, System.nanoTime())); + } + } + } + if (buffer.length() > 0) { + bundleDico.put(locale, buffer.substring(1)); + if (!silent) { + getLog().info("bundles for locale : " + bundleDico.get(locale)); + } + } + propertiesOut.store(bundleOut); + if (!silent && verbose) { + getLog().info("bundle created in " + PluginHelper.convertTime(t0, System.nanoTime()) + " (detected sentences : " + propertiesOut.size() + ")"); + } + if (checkBundle) { + checkBundle(locale, propertiesOut, showEmpty); + } + } + + // ecriture du ficher des definitions i18n (permet de faire une + // recherche extact sur un fichier puis d'en deduire les bundles a + // charger + String f = String.format(I18nBundleFactory.UNIQUE_BUNDLE_DEF, bundleOutputName); + File defOut = new File(bundleOutputDir, f); + if (!silent) { + getLog().info("prepare i18n definition " + defOut.getAbsolutePath()); + } + SortedProperties p = new SortedProperties(encoding, false); + p.setProperty(I18nBundleFactory.BUNDLE_DEF_LOCALES, bundles); + for (Entry<Locale, String> e : bundleDico.entrySet()) { + p.setProperty(I18nBundleFactory.BUNDLES_FOR_LOCALE + e.getKey().toString(), e.getValue()); + } + p.store(new FileOutputStream(defOut), null); + + if (!silent && verbose) { + getLog().info("done in " + PluginHelper.convertTime(t00, System.nanoTime())); + } + } catch (IOException e) { + getLog().error("File Error I/O ", e); + throw new MojoFailureException("File Error I/O "); + } + } + + /** + * Detecte les {@link I18nArtifact} et les retourne dans l'ordre de chargement + * dans le système i18n, i.e l'ordre des dependances entre artifacts. + * + * @return les artifacts i18nables triés par leur ordre de chargement dans le système i18n. + * + * @throws MalformedURLException + * @throws IOException + * @throws DependencyTreeBuilderException + */ + protected I18nArtifact[] detectI18nArtifacts() throws MalformedURLException, IOException, DependencyTreeBuilderException { + + Map<Artifact, I18nArtifact> dico = new java.util.HashMap<Artifact, I18nArtifact>(); + + I18nArtifact i18nArtifact; + for (Object o : project.getArtifacts()) { + i18nArtifact = new I18nArtifact((Artifact) o); + if (i18nArtifact.detectBundles()) { + if (!silent && getLog().isDebugEnabled()) { + getLog().debug("detected artifact " + i18nArtifact); + } + dico.put(i18nArtifact.getArtifact(), i18nArtifact); + } else { + if (!silent && getLog().isDebugEnabled()) { + getLog().debug("reject artifact " + i18nArtifact); + } + } + } + + ArtifactFilter artifactFilter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME); + + DependencyNode rootNode = dependencyTreeBuilder.buildDependencyTree(project, localRepository, factory, + artifactMetadataSource, artifactFilter, collector); + + List<Artifact> artifacts = new java.util.ArrayList<Artifact>(dico.keySet()); + + DependencyUtil.sortArtifacts(rootNode, artifacts, getLog().isDebugEnabled()); + + // l'artifact du projet est traite en dernier car s'il possède des + // bundles alors ils doivent etre charge en dernier + + Artifact projectArtifact = project.getArtifact(); + i18nArtifact = new I18nArtifact(projectArtifact, src.getParentFile()); + + if (i18nArtifact.detectBundles()) { + if (!silent && verbose) { + getLog().info("detected artifact " + i18nArtifact); + } + artifacts.add(i18nArtifact.getArtifact()); + dico.put(i18nArtifact.getArtifact(), i18nArtifact); + } + + I18nArtifact[] result = new I18nArtifact[artifacts.size()]; + int i = 0; + for (Artifact artifact : artifacts) { + result[i++] = dico.get(artifact); + } + return result; + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Generate.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Generate.java new file mode 100644 index 0000000..851af7f --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Generate.java @@ -0,0 +1,144 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.nuiton.util.PluginHelper.SortedProperties; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; + +/** + * Merge des fichiers de propriétés avec les anciens. + * + * @author julien + * @goal gen + * @phase generate-resources + * @execute goal=get + */ +public class Generate extends AbstractI18nPlugin { + + /** + * Un drapeau pour vérifier que les bundles ne contiennent pas d'entrées vides. + * + * @parameter expression="${i18n.checkBundle}" default-value="true" + * @required + * @since 1.0.0 + */ + protected boolean checkBundle; + /** + * Un drapeau pour afficher les entrées vides. (nécessite {@link #checkBundle} activé). + * + * @parameter expression="${i18n.showEmpty}" default-value="false" + * @required + * @since 1.0.0 + */ + protected boolean showEmpty; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + init(); + + if (!needGeneration()) { + getLog().info("Nothing to generate - all files are up to date."); + return; + } + + if (!silent) { + getLog().info("config - src basedir : " + src.getAbsolutePath()); + getLog().info("config - out basedir : " + out.getAbsolutePath()); + getLog().info("config - locales : " + Arrays.toString(locales)); + } + for (Locale locale : locales) { + if (!silent) { + getLog().info("prepare bundle for locale " + locale); + } + try { + // Merge + File bundleSrc = getI18nFile(src, artifactId, locale, false); + File bundleOut = getI18nFile(out, artifactId, locale, false); + + SortedProperties propertiesSrc = new SortedProperties(encoding); + + if (bundleSrc.exists()) { + propertiesSrc.load(bundleSrc); + } + + SortedProperties propertiesOut = new SortedProperties(encoding); + + if (!strictMode) { + // si on n'est pas en mode strict, on doit push back in bundle out, all the bundle src keys + propertiesOut.putAll(propertiesSrc); + } + propertiesOut.load(bundleOut); + + // Parcours des clés + for (Object key : propertiesOut.keySet()) { + Object oldKey = propertiesOut.get(key); + Object value = propertiesSrc.get(oldKey); + + // Récupération de la clé si elle a été renommée + if (!key.equals(oldKey) && value == null) { + value = propertiesSrc.get(key); + } + + if (value != null) { + propertiesOut.put(key, value); + } else { + propertiesOut.put(key, ""); + } + } + + //fixme : on devrait laisser le fichier en utf8 ? + //propertiesOut.store(bundleOut); + propertiesOut.store(new FileOutputStream(bundleOut)); + + // Sauvegarde avant copie + if (genSrc && keepBackup) { + backupFile(bundleSrc); + } + if (!silent) { + getLog().info("merge bundle " + locale + " to out"); + } + + if (checkBundle) { + checkBundle(locale, propertiesOut, showEmpty); + } + + if (genSrc) { + // Copie des fichiers dans les sources + copyFile(bundleOut, bundleSrc); + //if (verbose) { + if (!silent) { + getLog().info("copy bundle " + locale + " to src"); + } + //} + } + + } catch (IOException e) { + getLog().error("File Error I/O ", e); + throw new MojoFailureException("File Error I/O "); + } + } + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Getter.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Getter.java new file mode 100644 index 0000000..3bbaade --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/Getter.java @@ -0,0 +1,115 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.codehaus.plexus.util.DirectoryScanner; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; +import org.nuiton.util.PluginHelper; +import org.nuiton.util.PluginHelper.SortedProperties; + +/** + * Recupere les différents fichiers des parsers en un fichier de proprietes. + * + * @author julien + * @goal get + * @phase generate-resources + */ +public class Getter extends AbstractI18nPlugin { + + /* + * (non-Javadoc) + * @see org.apache.maven.plugin.AbstractMojo#execute() + */ + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + init(); + + if (!needGeneration()) { + if (verbose) { + getLog().info("Nothing to generate - all files are up to date."); + } + return; + } + if (!silent) { + getLog().info("config - basedir : " + out.getAbsolutePath()); + getLog().info("config - locales : " + Arrays.toString(locales)); + } + + try { + File bundleGetters = new File(out.getAbsolutePath() + File.separatorChar + artifactId + ".properties"); + bundleGetters.createNewFile(); + + DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir(out); + ds.setIncludes(new String[]{"*.getter"}); + ds.scan(); + String[] files = ds.getIncludedFiles(); + + // Fusion des fichiers propriétés des différents parsers + for (String file : files) { + long t0 = System.nanoTime(); + File bundleGetter = getGetterFile(out, file, false); + concactProperties(bundleGetter, bundleGetters); + if (genSrc) { + bundleGetter.delete(); + } + if (!silent) { + getLog().info("import getter " + bundleGetter.getName() + " in " + PluginHelper.convertTime(System.nanoTime() - t0)); + } + } + + // Création des bundles + for (Locale locale : locales) { + if (!silent && getLog().isDebugEnabled()) { + getLog().debug("generate bundle for locale " + locale); + } + File bundleOut = getI18nFile(out, artifactId, locale, false); + copyFile(bundleGetters, bundleOut); + if (!silent && verbose) { + getLog().info("generate bundle " + locale); + } + } + + bundleGetters.delete(); + } catch (IOException e) { + getLog().error("File Error I/O ", e); + throw new MojoFailureException("File Error I/O"); + } + } + + /** + * Concatene deux fichiers de proprietes + * + * @param in le fichier entrant + * @param out le fichier sortant + * @throws IOException si problème pendant la sauvegarde ou fichier non trouvé. + */ + protected void concactProperties(File in, File out) throws IOException { + SortedProperties propertiesIn = new SortedProperties(encoding).load(in); + SortedProperties propertiesOut = new SortedProperties(encoding).load(out); + propertiesOut.putAll(propertiesIn); + propertiesOut.store(out); + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nArtifact.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nArtifact.java new file mode 100644 index 0000000..d002f06 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nArtifact.java @@ -0,0 +1,100 @@ +/** + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.plugin; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.Locale; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.maven.artifact.Artifact; +import org.nuiton.i18n.bundle.I18nBundle; +import org.nuiton.i18n.bundle.I18nBundleEntry; +import org.nuiton.i18n.bundle.I18nBundleFactory; + +/** + * + * @author chemit + * @since 0.12 + */ +public class I18nArtifact { + + static final Log log = LogFactory.getLog(I18nArtifact.class); + protected final Artifact artifact; + protected final URL url; + protected I18nBundle[] bundles; + + public I18nArtifact(Artifact artifact) throws MalformedURLException { + this.artifact = artifact; + this.url = artifact.getFile().toURI().toURL(); + } + + public I18nArtifact(Artifact artifact, File file) throws MalformedURLException { + this.artifact = artifact; + this.url = file.toURI().toURL(); + } + + public Artifact getArtifact() { + return artifact; + } + + public URL getUrl() { + return url; + } + + public I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale) { + if (bundles == null) { + throw new NullPointerException("le bundleManager n'a pas ete initialise!"); + } + return I18nBundleFactory.getBundleEntries(l, defaultLocale, bundles); + } + + public boolean detectBundles() throws IOException { + + URL[] i18nUrls = I18nBundleFactory.getURLs(url); + + if (i18nUrls == null || i18nUrls.length == 0) { + // aucune url sur un fichier de traduction trouve + // l'artifact n'est pas i18n. + if (log.isDebugEnabled()) { + log.debug("no i18n url for artifact " + artifact); + } + return false; + } + + List<I18nBundle> listBundles = I18nBundleFactory.detectBundles(i18nUrls); + + if (listBundles.isEmpty()) { + // pas de bundle instancie (cela ne devrait jamais arrive...) + return false; + } + + this.bundles = listBundles.toArray(new I18nBundle[listBundles.size()]); + + return true; + } + + @Override + public String toString() { + return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion(); + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nLogger.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nLogger.java new file mode 100644 index 0000000..b1aceb0 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/I18nLogger.java @@ -0,0 +1,117 @@ +/** + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.plugin; + +import org.nuiton.util.SourceEntry; +import org.apache.maven.plugin.logging.SystemStreamLog; + +import java.beans.Introspector; +import java.io.File; +import org.nuiton.util.PluginHelper; +/** + * Le logger utilisé par les mojo. + * + * @author chemit + * @since 0.9 + */ +public class I18nLogger extends SystemStreamLog { + + /** l'entrée en cours de traitement (pour les parseurs) */ + private SourceEntry entry; + + /** le fichier en cours de traitement (pour les parseurs) */ + protected File file; + + /** le prefix du mojo courant a ajouter dans les logs. */ + protected String parser; + + public I18nLogger(AbstractI18nPlugin parser) { + this.parser = "i18n:" + Introspector.decapitalize(parser.getClass().getSimpleName()) + " on " + parser.getArtifactId(); + } + + @Override + public void info(CharSequence content) { + print(0, "INFO", null, content.toString()); + } + + @Override + public void debug(CharSequence content) { + print(0, "DEBUG", null, content.toString()); + } + + public void infoEntry(String action, CharSequence content) { + print(0, "INFO", action, entry.toString() + (content == null ? "" : " - " + content.toString())); + } + + public void infoFile(String action, String content) { + print(2, "INFO", action, file.toString() + (content == null ? "" : " - " + content)); + } + + public void infoAction(String action, String content) { + print(2, "INFO", action, (content == null ? "" : " - " + content)); + } + + private void print(int start, String prefix, String context, String content) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(prefix).append("] [").append(parser).append("] "); + + for (int i = 0; i < start; i++) { + sb.append(' '); + } + if (context != null) { + sb.append("<").append(context).append("> "); + } + sb.append(content); + System.out.println(sb.toString()); + } + + public void setEntry(SourceEntry entry) { + this.entry = entry; + } + + /** + * Construit une chaine de log formatée. + * + * @param msg le prefix du message + * @param nbFiles le nombre de fichiers actuellement traités + * @param time le time de traitement de ce fichier + * @param all le temps de traitement de tous les fichiers + * @return la chaine de log formatée + */ + public String getLogEntry(String msg, int nbFiles, long time, long all) { + long now = System.nanoTime(); + long delta = now - time; + String s = msg; + if (time > 0) { + s += " (" + PluginHelper.convertTime(delta) + ")"; + } + if (all > 0) { + s += "(total time:" + PluginHelper.convertTime(now - all) + ")"; + } + if (nbFiles > 0) { + s += " ( ~ " + PluginHelper.convertTime(((now - all) / (nbFiles))) + " / file)"; + } + return s; + } + + public void setFile(File file) { + this.file = file; + } + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParser.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParser.java new file mode 100644 index 0000000..d484ad7 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/AbstractI18nParser.java @@ -0,0 +1,369 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin.parser; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.nuiton.i18n.plugin.AbstractI18nPlugin; +import org.nuiton.i18n.plugin.I18nLogger; +import org.nuiton.util.PluginHelper.SortedProperties; +import org.nuiton.i18n.plugin.parser.event.KeysModifier; +import org.nuiton.util.FileUpdater; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.nuiton.util.SourceEntry; + +/** + * Abstract implementation for parsing goal. + * + * @author tony + */ +public abstract class AbstractI18nParser extends AbstractI18nPlugin implements Parser { + + /** @return the outGetter to use for the instance (java.getter,...) */ + protected abstract String getOutGetter(); + + /** @return the starting regex expression to catch keys in key modifier */ + protected abstract String getKeyModifierStart(); + + /** @return the ending regex expression to catch keys in key modifier */ + protected abstract String getKeyModifierEnd(); + + /** @return the default includes to add to directory scanner */ + protected abstract String[] getDefaultIncludes(); + + /** @return the default excludes to add to directory scanner */ + protected abstract String[] getDefaultExcludes(); + + /** @return the default src directory to use in directory scanner */ + protected abstract File getDefaultBasedir(); + + public abstract FileUpdater newFileUpdater(SourceEntry entry); + /** + * treate default entry + * + * @parameter expression="${i18n.treateDefaultEntry}" default-value="true" + */ + protected boolean treateDefaultEntry; + /** + * Source entries (src+includes+excludes) . + * + * @parameter expression="${i18n.entries}" + */ + protected MySourceEntry[] entries; + /** + * flag to display touched files while parsing. + * <p/> + * Note: the value will be always <code>true</code> if {@link #verbose} is set + * at <code>true</code>. + * + * @parameter expression="${i18n.showTouchedFiles}" default-value="${maven.verbose}" + * @since 0.9 + */ + protected boolean showTouchedFiles; + /** + * flag to save at eachfile treated the getter file + * + * @parameter expression="${i18n.safeMode}" default-value="false" + * @since 0.9 + */ + protected boolean safeMode; + protected SortedProperties result; + protected SortedProperties oldParser; + protected SortedProperties oldLanguage; + protected int fileTreated = 0; + protected long t0; + protected boolean touchFile; + protected List<File> treadedFiles; + + @Override + public void init() { + super.init(); + t0 = System.nanoTime(); + result = new SortedProperties(encoding); + oldParser = new SortedProperties(encoding); + oldLanguage = new SortedProperties(encoding); + out.mkdirs(); + // evenements + if (keysModifier) { + addParserEvent(KeysModifier.getInstance(getKeyModifierStart(), getKeyModifierEnd(), encoding)); + } + treadedFiles = new ArrayList<File>(); + if (!silent && verbose) { + showTouchedFiles = true; + } + } + + + /* + * (non-Javadoc) + * @see org.apache.maven.plugin.AbstractMojo#execute() + */ + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + init(); + if (entries == null || entries.length == 0 && !treateDefaultEntry) { + // nothing to do + return; + } + + if (!silent && safeMode) { + getLog().info("config - safeMode is on (could be slower)."); + } + if (!silent && strictMode) { + getLog().info("config - strictMode is on (all files will be parsed)."); + } + + try { + // Reprise sur un ancien parsing + File oldParserFile = getGetterFile(out, getOutGetter(), true); + File saveFile = getGetterFileBackup(out, getOutGetter()); + + oldParser.load(oldParserFile); + copyFile(oldParserFile, saveFile); +// FileUtil.copy(oldParserFile, saveFile); + + // Anciennes cles disponnibles + //fixme : pourquoi on utilise un bundle precis ? le premier ici, je ne comprends pas + File oldLanguageFile = getI18nFile(src, artifactId, locales[0], true); + + oldLanguage.load(oldLanguageFile); + + // Parsing + parse(); + + // Suppression du fichier sauvegarder + saveFile.delete(); + + int i = treadedFiles.size(); + if (fileTreated == 0) { + if (!silent) { + getLog().info("Nothing to generate - all files are up to date."); + } + } else { + if (!silent) { + getLog().info(getVerboseLog().getLogEntry("parsing is done. [treated file(s) : " + i + '/' + fileTreated + "]", fileTreated, 0, t0)); + } + addGetter(); + } + + } catch (Exception e) { + getLog().error("Error code parsing ", e); + throw new MojoFailureException("Error code parsing"); + } + + } + + /** + * launch the parse on every given entries. + * + * @throws IOException if any io pb + */ + @Override + public void parse() throws IOException { + if (treateDefaultEntry) { + addDefaultEntry(); + } + long t00 = System.nanoTime(); + for (MySourceEntry entry : this.entries) { + I18nLogger vLog = getVerboseLog(); + + vLog.setEntry(entry); + + boolean skip = entry.init(this); + + if (skip) { + if (!silent && verbose) { + getLog().info("skip - " + entry.getSkipMessage()); + } + continue; + } + + long t000 = System.nanoTime(); + int nbFiles = entry.getFiles().length; + if (!silent && verbose) { + vLog.infoEntry("start", vLog.getLogEntry("[incoming file(s) : " + entry.getFoudFiles() + "]", 0, 0, 0)); + } + + // launch parser for found files + parseEntry(entry); + + if (!silent && verbose) { + // log skipped files + for (String skipFile : entry.getSkipFiles()) { + vLog.setFile(new File(entry.getBasedir(), skipFile)); + vLog.infoFile("skip", null); + } + } + fileTreated += nbFiles; + if (!silent && verbose) { + vLog.infoEntry("end", vLog.getLogEntry("[treated file(s) : " + nbFiles + "]", nbFiles, t000, t00)); + } + t00 = System.nanoTime(); + } + } + + /** + * Add the default entry to entries given in configuration. + * <p/> + * This is a convinient method to simplify the configuration of the plugin. + */ + protected void addDefaultEntry() { + List<MySourceEntry> list; + + if (entries == null || entries.length == 0) { + list = new ArrayList<MySourceEntry>(); + } else { + list = new ArrayList<MySourceEntry>(Arrays.asList(entries)); + } + list.add(new MySourceEntry()); + entries = list.toArray(new MySourceEntry[list.size()]); + } + + /** + * launch parsing on a given entry. + * + * @param entry currentEntry to treate + * @throws IOException if any io pb. + */ + protected final void parseEntry(SourceEntry entry) throws IOException { + long t00 = System.nanoTime(); + String[] files = entry.getFiles(); + int beforeEntryResultSize = result.size(); + for (int i = 0, max = files.length; i < max; i++) { + String file1 = files[i]; + long t000 = System.nanoTime(); + String fileName = entry.getBasedir().getAbsolutePath() + File.separator + file1; + File file = new File(fileName); + for (ParserEvent event : events) { + event.eventChangeFile(file); + } + I18nLogger vLog = getVerboseLog(); + vLog.setFile(file); + + touchFile = false; + int size = result.size(); + if (!silent && verbose) { + vLog.infoFile("parse", null); + } + parseFile(file); + + //TC-20090214 pour des questions de performance, on ne sauvegarde pas + // a chaque traitement de fichier, les clefs mais une fois pour chaque + // source entry + // Detection de nouvelles cles, sauvegarde du fichier pour pouvoir le restaurer en cas de plantage + if (safeMode) { + if (size != result.size()) { + saveGetterFile(); + } + } + if (touchFile) { + if (showTouchedFiles) { + vLog.infoFile("touch", null); + } + treadedFiles.add(file); + if (getLog().isDebugEnabled()) { + vLog.debug(vLog.getLogEntry(fileName, i, t000, t00)); + } + } + for (ParserEvent event : events) { + event.eventNextFile(file); + } + } + + if (!safeMode && beforeEntryResultSize < result.size()) { + // Detection de nouvelles cles, sauvegarde du fichier + saveGetterFile(); + } + } + + /** + * Save the result in the getter file. + * + * @throws IOException if any io pb + */ + protected void saveGetterFile() throws IOException { + File getterFile = getGetterFile(out, getOutGetter(), false); + result.store(getterFile); + } + + public static class MySourceEntry extends SourceEntry { + + public boolean init(AbstractI18nParser mojo) { + if (!useForGoal(mojo.getClass().getSimpleName())) { + // skip not for this goal + skipMessage = "exclude for this goal."; + return true; + } + + String[] filesForEntry = getFilesForEntry(mojo); + + if (filesForEntry.length == 0) { + // skip no file found + skipMessage = "no file found."; + return true; + } + setUpdater(mojo.newFileUpdater(this)); + + if (mojo.strictMode || updater == null) { + // mojo strict mode or not updater, so force all files + skipFiles = new String[0]; + this.files = filesForEntry; + return false; + } + + List<String> listFiles = new ArrayList<String>(); + List<String> listSkipFiles = new ArrayList<String>(); + + // test if have any file + for (String foundFile : filesForEntry) { + File file = new File(getBasedir(), foundFile); + if (isFileUptodate(file)) { + listSkipFiles.add(foundFile); + } else { + listFiles.add(foundFile); + } + } + boolean todo = !listFiles.isEmpty(); + if (!todo) { + // skip, no file out-of -date + skipMessage = "all files are up to date."; + this.skipFiles = listSkipFiles.toArray(new String[listSkipFiles.size()]); + this.files = new String[0]; + return true; + } + this.skipFiles = listSkipFiles.toArray(new String[listSkipFiles.size()]); + this.files = listFiles.toArray(new String[listFiles.size()]); + return false; + } + + /** + * Obtain all the relative path of files to treate for a given entry. + * + * @param mojo the given mojo + * @return the list of relative path of files for the given entry + */ + protected String[] getFilesForEntry(AbstractI18nParser mojo) { + return getIncludedFiles(mojo.getDefaultBasedir(), mojo.getDefaultIncludes(), mojo.getDefaultExcludes()); + } + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/Parser.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/Parser.java new file mode 100644 index 0000000..5ba3c6b --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/Parser.java @@ -0,0 +1,55 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser; + +import java.io.File; +import java.io.IOException; + +/** + * Interface type pour la définition d'un nouveau parser. + * <p/> + * Une implantation abstraite est proposée : {@link AbstractI18nParser}. + * + * @author julien + */ +public interface Parser { + + /** + * Lancement du parser + * + * @throws java.io.IOException if any io pb + */ + public void parse() throws IOException; + + /** + * Parse sur un fichier + * + * @param file le fichier à parser + */ + public void parseFile(File file); + + /** + * Parse une partie du fichier + * + * @param file le fichier à parser + * @param args ? TODO + */ + public void parseLine(File file, String args); + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserEvent.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserEvent.java new file mode 100644 index 0000000..041a3f9 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserEvent.java @@ -0,0 +1,59 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser; + +import java.io.File; + +/** + * Permet d'ajouter des évènements sur les parsers + * + * @author julien + */ +public interface ParserEvent { + + /** + * M�thode appelée quand on change de fichier parsé + * + * @param file + */ + public void eventChangeFile(File file); + + /** + * Méthode appelée après le parsing du fichier + * + * @param file + */ + public void eventNextFile(File file); + + /** + * M�thode appelée quand on change de clé + * + * @param keyI18n + * @param newKey + */ + public void eventChangeKey(String keyI18n, boolean newKey); + + /** + * M�thode appelée pour récupérer la nouvelle valeur de clé + * + * @return + */ + public String eventGetRealKey(); + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserException.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserException.java new file mode 100644 index 0000000..6abd0cd --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserException.java @@ -0,0 +1,45 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser; + +/** + * Permet la gestion des exceptions dans les parsers et dans les évènements + * + * @author julien + */ +public class ParserException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ParserException() { + super(); + } + + public ParserException(String message, Throwable cause) { + super(message, cause); + } + + public ParserException(String message) { + super(message); + } + + public ParserException(Throwable cause) { + super(cause); + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/event/KeysModifier.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/event/KeysModifier.java new file mode 100644 index 0000000..775c033 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/event/KeysModifier.java @@ -0,0 +1,219 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser.event; + +import org.nuiton.i18n.plugin.parser.ParserEvent; +import org.nuiton.i18n.plugin.parser.ParserException; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTextField; +import java.awt.Container; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.nuiton.util.PluginHelper; + +/** + * IHM permettant de modifier les clés de traduction en direct dans les fichiers + * parsés et les fichiers de propriétés. + * + * @author julien + */ +public class KeysModifier extends JFrame implements ParserEvent { + + private static final long serialVersionUID = 1L; + + // Modification des clés dans le fichier + protected List<String> newKeys; + protected boolean needModifiedFile; + protected String patternLeft; + protected String patternRight; + protected String encoding; + + // Interface + protected JLabel name = new JLabel(); + protected JLabel path = new JLabel(); + protected JTextField key = new JTextField(); + protected JTextField pattern = new JTextField(".*"); + protected JCheckBox onlyNewKey = new JCheckBox(); + + + protected JButton next = new JButton("Next >>"); + private static KeysModifier keysModifier; + + /** + * Récupération d'une instance de l'interface + * + * @param patternLeft left pattern + * @param patternRight right pattern + * @param encoding encoding + * @return the shared instance with new config + */ + public static KeysModifier getInstance(String patternLeft, String patternRight, String encoding) { + if (keysModifier == null) { + keysModifier = new KeysModifier(); + } + + + keysModifier.encoding = encoding; + keysModifier.patternLeft = patternLeft; + keysModifier.patternRight = patternRight; + + return keysModifier; + } + + /** Contructeur de l'interface */ + private KeysModifier() { + setLayout(new GridLayout(9, 2, 10, 10)); + + Container pane = getContentPane(); + pane.add(new JLabel("--- File information ---")); + pane.add(new JLabel()); + + pane.add(new JLabel("Name :")); + pane.add(name); + + pane.add(new JLabel("Path : ")); + pane.add(path); + + pane.add(new JLabel("--- Files language ---")); + pane.add(new JLabel()); + + pane.add(new JLabel("Key :")); + pane.add(key); + + pane.add(new JLabel("--- Filters ---")); + pane.add(new JLabel()); + + pane.add(new JLabel("Pattern :")); + pane.add(pattern); + + pane.add(new JLabel("Only new key :")); + pane.add(onlyNewKey); + + pane.add(new JLabel()); + pane.add(next); + + next.addActionListener(new EventNextKey()); + addWindowListener(new EventWindows()); + + setTitle("Keys modifier"); + setSize(800, 400); +// pack(); + setVisible(true); + } + + @Override + public void eventChangeFile(File file) { + name.setText(file.getName()); + path.setText(file.getPath()); + key.setText(""); + repaint(); + + newKeys = new ArrayList<String>(); + needModifiedFile = false; + } + + @Override + public void eventNextFile(File file) { + if (needModifiedFile) { + String content; + int region = 0; + + try { + content = PluginHelper.readAsString(file, encoding); + } catch (IOException e) { + throw new ParserException(e); + } + + for (Iterator<String> iterator = newKeys.iterator(); iterator.hasNext();) { + String oldKey = iterator.next(); + String realKey = iterator.next(); + Pattern p = Pattern.compile("(" + patternLeft + ")(" + Pattern.quote(oldKey) + ")(" + patternRight + ")"); + Matcher matcher = p.matcher(content); + matcher.region(region, content.length()); + matcher.find(); + region = matcher.start(); + content = matcher.replaceFirst("$1" + realKey + "$3"); + } + + try { + PluginHelper.writeString(file, content, encoding); + } catch (IOException e) { + throw new ParserException(e); + } + } + } + + @Override + public synchronized void eventChangeKey(String keyI18n, boolean newKey) { + key.setText(keyI18n); + newKeys.add(key.getText()); + repaint(); + if (isVisible() && keyI18n.matches(pattern.getText()) && (!onlyNewKey.isSelected() || newKey)) { + try { + wait(); + } catch (InterruptedException e) { + throw new ParserException(e); + } + } + } + + @Override + public String eventGetRealKey() { + newKeys.add(key.getText()); + needModifiedFile |= !newKeys.get(newKeys.size() - 1).equals(newKeys.get(newKeys.size() - 2)); + return key.getText(); + } + + /** Action sur le boutton pour passer � la cl� suivante */ + class EventNextKey implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + eventNextKey(); + } + } + + /** Action sur la fermeture de la frame */ + class EventWindows extends WindowAdapter { + + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + eventNextKey(); + } + } + + /** Permet de passer à la clé suivante */ + public synchronized void eventNextKey() { + notifyAll(); + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJava.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJava.java new file mode 100755 index 0000000..f4bba34 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJava.java @@ -0,0 +1,164 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser.impl; + +import org.apache.maven.project.MavenProject; +import org.nuiton.util.SourceEntry; +import org.nuiton.i18n.plugin.parser.AbstractI18nParser; +import org.nuiton.i18n.plugin.parser.ParserEvent; +import org.nuiton.i18n.plugin.parser.ParserException; +import org.nuiton.util.FileUpdater; +import org.nuiton.util.FileUpdaterHelper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import org.nuiton.processor.filters.I18nFilter; + +/** + * Récupération des chaine a traduire depuis les fichiers java. + * + * @author julien + * @goal parserJava + * @phase generate-resources + */ +public class ParserJava extends AbstractI18nParser { + + /** + * Source entries (src+includes+excludes) . + * + * @parameter expression="${i18n.defaultIncludes}" default-value="**\\/*.java" + */ + protected String defaultIncludes; + + /** + * default src for an entry. + * + * @parameter expression="${i18n.defaultBasedir}" default-value="${basedir}/src/main/java" + */ + protected File defaultBasedir; + + /** + * Dependance du projet. + * + * @parameter default-value="${project}" + * @readonly + */ + protected MavenProject project; + + /** + * Repertoire sources des fichiers i18n. + * + * @parameter expression="${i18n.cp}" default-value="${basedir}/target/classes" + * @required + */ + protected File cp; + + @Override + public String[] getDefaultIncludes() { + return new String[]{defaultIncludes}; + } + + @Override + public String[] getDefaultExcludes() { + return new String[]{}; + } + + @Override + public File getDefaultBasedir() { + return defaultBasedir; + } + + @Override + public FileUpdater newFileUpdater(SourceEntry entry) { + return FileUpdaterHelper.newJavaFileUpdater(entry.getBasedir(), cp); + } + + @Override + protected String getKeyModifierStart() { + return "_\\(\\s*\""; + } + + @Override + protected String getKeyModifierEnd() { + return "\"\\s*(\\)|,|\\+|$)"; + } + + @Override + protected String getOutGetter() { + return "java.getter"; + } + + protected I18nFilter filter; + + @Override + public void init() { + super.init(); + filter = new I18nFilter(); + } + + @Override + public void parseFile(File srcFile) { + LineNumberReader lnr=null; + String line=null; + try { + lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(srcFile))); + + while (lnr.ready()) { + line = lnr.readLine(); + parseLine(srcFile, line); + } + + } catch (Exception e) { + if (line!=null) { + getLog().error("could not parse line "+line); + } + throw new ParserException(e); + } + } + + /* + * (non-Javadoc) + * @see org.codelutin.i18n.plugin.core.Parser#parseLine(java.io.File, java.lang.String) + */ + @Override + public void parseLine(File srcFile, String line) { + String keysSet = filter.parse(line); + + if (!keysSet.equals(I18nFilter.EMPTY_STRING)) { + touchFile = true; + // Found a set of i18n Strings, split it. + String[] keys = keysSet.split("="); + for (String key : keys) { + String keyModified = key; + for (ParserEvent event : events) { + event.eventChangeKey(key, !oldLanguage.containsKey(key)); + keyModified = event.eventGetRealKey(); + } + if (oldParser.containsKey(key)) { + result.put(keyModified, oldParser.get(key)); + } else { + result.put(keyModified, key); + } + } + } + } + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaActionConfig.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaActionConfig.java new file mode 100644 index 0000000..9a4bb23 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaActionConfig.java @@ -0,0 +1,87 @@ +/* +* *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin.parser.impl; + +import org.nuiton.i18n.plugin.parser.ParserEvent; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Un parseur java pour scanner les annotations ActionConfig + * + * @author chemit + * @goal parserJavaActionConfig + * @phase generate-resources + */ +public class ParserJavaActionConfig extends ParserJava { + + protected static final Pattern MATCH_PATTERN = Pattern.compile("(name|shortDescription|longDescription|name2|shortDescription2|longDescription2)\\s*=\\s*\"([\\w|\\.]+)\"(|\\s*|\\s*,\\s*$)"); + + @Override + protected String getKeyModifierStart() { + return "[\\w|\\.]+\\s*=\\s*\""; + } + + @Override + protected String getKeyModifierEnd() { + return "\"\\s*(\\)|,|\\+|$)"; + } + + @Override + protected String getOutGetter() { + return "java-action-config.getter"; + } + + /* + * (non-Javadoc) + * @see org.codelutin.i18n.plugin.extension.XmlParser#extract(java.lang.String) + */ + + public String extract(String i18nString) { + Matcher matcher = MATCH_PATTERN.matcher(i18nString.trim()); + if (matcher.matches()) { + return matcher.group(2); + } + return null; + } + + /* + * (non-Javadoc) + * @see org.codelutin.i18n.plugin.core.Parser#parseLine(java.io.File, java.lang.String) + */ + @Override + public void parseLine(File srcFile, String line) { + String key = extract(line); + if (key != null) { + touchFile = true; + String keyModified = key; + for (ParserEvent event : events) { + event.eventChangeKey(key, !oldLanguage.containsKey(key)); + keyModified = event.eventGetRealKey(); + } + if (oldParser.containsKey(key)) { + result.put(keyModified, oldParser.get(key)); + } else { + result.put(keyModified, key); + } + } + } + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaTabConfig.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaTabConfig.java new file mode 100644 index 0000000..5baf048 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJavaTabConfig.java @@ -0,0 +1,35 @@ +/** + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.plugin.parser.impl; + +/** + * Un parseur java pour scanner les annotations TabContentConfig. + * + * @author chemit + * @goal parserJavaTabConfig + * @phase generate-resources + */ +public class ParserJavaTabConfig extends ParserJavaActionConfig { + + @Override + protected String getOutGetter() { + return "java-tab-config.getter"; + } + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJaxx.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJaxx.java new file mode 100644 index 0000000..e7054bb --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserJaxx.java @@ -0,0 +1,113 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser.impl; + +import org.nuiton.util.SourceEntry; +import org.nuiton.util.FileUpdater; +import org.nuiton.util.FileUpdaterHelper; + +import java.io.File; + +/** + * Récupération des chaine à traduire depuis les fichiers xml Jaxx. + * + * @author julien + * @goal parserJaxx + * @phase generate-resources + * @deprecated since 1.0.0, prefer use the java parser since xml parser can not + * be easly extendable + */ +@Deprecated +public class ParserJaxx extends ParserXml { + + /** + * Source entries (src+includes+excludes) . + * + * @parameter expression="${i18n.defaultIncludes}" default-value="**\\/*.jaxx" + */ + protected String defaultIncludes; + + /** + * Where jaxx files should have been generated. + * + * @parameter expression="${i18n.defaultGenerateBasedir}" default-value="${basedir}/target/generated-sources/java" + */ + protected File defaultGenerateBasedir; + + /** + * Regles xml. + * + * @parameter expression="${i18n.rulesJaxx}" default-value="jaxx.rules" + */ + protected String rulesJaxx; + + @Override + public String[] getDefaultIncludes() { + return new String[]{defaultIncludes}; + } + + @Override + public String[] getDefaultExcludes() { + return new String[]{}; + } + + @Override + public FileUpdater newFileUpdater(SourceEntry entry) { + return FileUpdaterHelper.newJavaFileUpdater(entry.getBasedir(), defaultGenerateBasedir); + } + + @Override + protected String getOutGetter() { + return "jaxx.getter"; + } + + @Override + protected String getKeyModifierStart() { + return "=\\s*[\"\']"; + } + + @Override + protected String getKeyModifierEnd() { + return "[\"\']"; + } + + @Override + protected String getFileRules() { + return rulesJaxx; + } + + @Override + protected String getCoreFileRules() { + return "jaxx.rules"; + } + + public void setRulesJaxx(String rulesJaxx) { + this.rulesJaxx = rulesJaxx; + } + + /* + * (non-Javadoc) + * @see org.codelutin.i18n.plugin.extension.XmlParser#extract(java.lang.String) + */ + @Override + public String extract(String i18nString) { + return i18nString.length() == 0 ? null : i18nString; + } + +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserSwixat.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserSwixat.java new file mode 100644 index 0000000..a5b311a --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserSwixat.java @@ -0,0 +1,104 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.i18n.plugin.parser.impl; + +import org.nuiton.util.SourceEntry; +import org.nuiton.util.FileUpdater; + +/** + * Recuperation des chaines à traduire depuis les fichiers xml Swixat. + * + * @author julien + * @goal parserSwixat + * @phase generate-resources + * @deprecated since 1.0.0 : swixat is dead... + */ +@Deprecated +public class ParserSwixat extends ParserXml { + + /** + * Source entries (src+includes) . + * + * @parameter expression="${i18n.defaultIncludes}" default-value="**\\/*.xml" + */ + protected String defaultIncludes; + + /** + * Source entries (src+excludes) . + * + * @parameter expression="${i18n.defaultIncludes}" default-value="**\\/context.xml" + */ + protected String defaultExcludes; + + /** + * Regles xml. + * + * @parameter expression="${i18n.rulesSwixat}" default-value="swixat.rules" + */ + protected String rulesSwixat; + + @Override + public String[] getDefaultIncludes() { + return new String[]{defaultIncludes}; + } + + @Override + public String[] getDefaultExcludes() { + return new String[]{defaultExcludes}; + } + + @Override + public FileUpdater newFileUpdater(SourceEntry entry) { + return null; + } + + @Override + protected String getKeyModifierStart() { + return "=\\s*[\"\']"; + } + + @Override + protected String getKeyModifierEnd() { + return "[\"\']"; + } + + @Override + protected String getOutGetter() { + return "swixat.getter"; + } + + @Override + protected String getFileRules() { + return rulesSwixat; + } + + @Override + protected String getCoreFileRules() { + return "swixat.rules"; + } + + /* + * (non-Javadoc) + * @see org.codelutin.i18n.plugin.extension.XmlParser#extract(java.lang.String) + */ + @Override + public String extract(String i18nString) { + return i18nString; + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserValidation.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserValidation.java new file mode 100644 index 0000000..d080ee8 --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserValidation.java @@ -0,0 +1,194 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin.parser.impl; + +import java.io.IOException; +import java.net.URLConnection; +import org.nuiton.util.SourceEntry; +import org.nuiton.util.FileUpdater; +import org.nuiton.util.MirroredFileUpdater; + +import java.io.File; +import java.net.SocketTimeoutException; +import java.net.URL; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Récupération des chaine à traduire depuis les fichiers xml de validation. + * <p/> + * Le goal doit etre execute avant que les resources soient copiees dans target/classes + * pour rendre operatne le file updater (sinon lesfichiers sont toujours a jour...) + * + * @author chemit + * @goal parserValidation + * @phase generate-resources + */ +public class ParserValidation extends ParserXml { + + public static final String XWORK_PUBLIC_ID = "-//OpenSymphony Group//XWork Validator 1.0.2//EN"; + final URL xworksResource = getClass().getResource("/xwork-validator-1.0.2.dtd"); + /** + * Source entries (src+includes+excludes) . + * + * @parameter expression="${i18n.defaultIncludes}" default-value="**\\/**-validation.xml" + */ + protected String defaultIncludes; + /** + * Where jaxx files should have been generated. + * + * @parameter expression="${i18n.cp}" default-value="${basedir}/target/classes" + */ + protected File cp; + /** + * Regles xml. + * + * @parameter expression="${i18n.rulesValidation}" default-value="validation.rules" + */ + protected String rulesValidation; + /** + * default src for an entry. + * + * @parameter expression="${i18n.defaultBasedir}" default-value="${basedir}/src/main/resources" + * @required + */ + protected File defaultBasedir; + /** + * Always use the local xowrks dtd to increase performance. + * + * @parameter expression="${i18n.useLocalResolver}" default-value="true" + * @since 1.6.0 + */ + protected boolean useLocalResolver; + + @Override + public String[] getDefaultIncludes() { + return new String[]{defaultIncludes}; + } + + @Override + public String[] getDefaultExcludes() { + return new String[]{}; + } + + @Override + public File getDefaultBasedir() { + return defaultBasedir; + } + + @Override + public FileUpdater newFileUpdater(SourceEntry entry) { + return new MirroredFileUpdater("", "", entry.getBasedir(), this.cp) { + + @Override + public File getMirrorFile(File f) { + String file = f.getAbsolutePath().substring(this.prefixSourceDirecotory); + return new File(this.destinationDirectory + File.separator + file); + } + }; + } + + @Override + protected String getOutGetter() { + return "validation.getter"; + } + + @Override + protected String getKeyModifierStart() { + return "=\\s*[\"\']"; + } + + @Override + protected String getKeyModifierEnd() { + return "[\"\']"; + } + + @Override + protected String getFileRules() { + return rulesValidation; + } + + @Override + protected String getCoreFileRules() { + return "validation.rules"; + } + +// public void setRulesJaxx(String rulesJaxx) { +// this.rulesValidation = rulesJaxx; +// } + + @Override + public String extract(String i18nString) { + String s = null; + if (!i18nString.trim().isEmpty()) { + s = i18nString.trim(); + int end = s.indexOf("##"); + if (end > 0) { + // remove params from key + s = s.substring(0, end); + } + } + if (getLog().isDebugEnabled()) { + getLog().debug(i18nString + " = " + s); + } + return s; + } + + @Override + public EntityResolver getEntityResolver() { + + return new EntityResolver() { + + public static final String XWORK_PUBLIC_ID = "-//OpenSymphony Group//XWork Validator 1.0.2//EN"; + boolean useLocal = useLocalResolver; + + @Override + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + if (getLog().isDebugEnabled()) { + getLog().debug("publicID:" + publicId + ", systemId:" + systemId); + } + if (XWORK_PUBLIC_ID.equals(publicId)) { + if (!useLocal) { + URL uri = new URL(systemId); + if (verbose) { + getLog().info("try to connect to " + uri); + } + URLConnection openConnection = uri.openConnection(); + openConnection.setUseCaches(true); + openConnection.setConnectTimeout(1000); + try { + openConnection.connect(); + return new InputSource(openConnection.getInputStream()); + } catch (SocketTimeoutException e) { + useLocal = true; + } catch (IOException e) { + useLocal = true; + } + } + + // use directly local resource + InputSource inputSource = new InputSource(xworksResource.openStream()); + return inputSource; + } + // use the default behaviour + return null; + } + }; + } +} diff --git a/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserXml.java b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserXml.java new file mode 100644 index 0000000..8b78c3a --- /dev/null +++ b/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/impl/ParserXml.java @@ -0,0 +1,255 @@ +/* + * *##% Plugin maven pour i18n + * Copyright (C) 2007 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.plugin.parser.impl; + +import javax.xml.parsers.ParserConfigurationException; +import org.nuiton.i18n.plugin.parser.AbstractI18nParser; +import org.nuiton.i18n.plugin.parser.ParserEvent; +import org.nuiton.i18n.plugin.parser.ParserException; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.xml.sax.EntityResolver; + +/** + * Récupération des chaines à traduire depuis les fichiers xml. + * + * @author julien + */ +public abstract class ParserXml extends AbstractI18nParser { + + /** Taille du buffer pour les lectures/écritures */ + protected static final int BUFFER_SIZE = 8 * 1024; + /** + * default src for an entry. + * + * @parameter expression="${i18n.defaultBasedir}" default-value="${basedir}/src/main/uimodel" + * @required + */ + protected File defaultBasedir; + protected String rules; + protected XPathFactory factory; + protected XPath xpath; + protected DocumentBuilder builder; + + /** + * Fonction d'extraction de la chaine + * + * @param i18nString le clef i18n + * @return la chaine + */ + public abstract String extract(String i18nString); + + /** @return le fichier des rules */ + protected abstract String getFileRules(); + + /** @return le fichier des rules de base à toujours charger */ + protected abstract String getCoreFileRules(); + + @Override + public void init() { + super.init(); + this.factory = XPathFactory.newInstance(); + this.rules = getRules(getFileRules()); + this.xpath = factory.newXPath(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); // never forget this! + + try { + // never forget this! + builder = documentBuilderFactory.newDocumentBuilder(); + + EntityResolver resolver = getEntityResolver(); + + if (resolver != null) { + builder.setEntityResolver(resolver); + } + + } catch (ParserConfigurationException ex) { + throw new IllegalStateException("could not load DocumentBuilder for reason " + ex.getMessage(), ex); + } + } + + public EntityResolver getEntityResolver() { + return null; + } + + public InputSource getSystemId(String publicId) { + return null; + } + + @Override + public void parseFile(File file) { + NodeList list; +// InputSource inputSource = new InputSource(file.getAbsolutePath()); // TODO: A deplacer pour les performances + + try { + int size = result.size(); + + // Recherche des clés à partir d'un xpath + + Document doc = builder.parse(file.getAbsolutePath()); +// Document doc = builder.parse(new FileInputStream(file.getAbsolutePath()),xworksResource.getFile()); + XPathExpression expression = xpath.compile(rules); + list = (NodeList) expression.evaluate(doc, XPathConstants.NODESET); +// list = (NodeList) expression.evaluate(inputSource, XPathConstants.NODESET); + + for (int index = 0; index < list.getLength(); index++) { + Node node = list.item(index); + parseLine(file, node.getTextContent()); + } + if (safeMode) { + // Détection de nouvelles clés, sauvegarde du fichier pour pouvoir le restaurer en cas de plantage + if (size != result.size()) { + saveGetterFile(); + } + } + } catch (Exception e) { + throw new ParserException(e); + } + } + + @Override + public void parseLine(File file, String key) { + key = extract(key); + if (key != null) { + touchFile = true; + String keyModified = key; + for (ParserEvent event : events) { + event.eventChangeKey(key, !oldLanguage.containsKey(key)); + keyModified = event.eventGetRealKey(); + } + + if (oldParser.containsKey(key)) { + result.put(keyModified, oldParser.get(key)); + } else { + result.put(keyModified, key); + } + } + } + + @Override + public File getDefaultBasedir() { + return defaultBasedir; + } + + /** + * Récupère le xpath à partir d'un fichier + * + * @param fileRules le nom du fichier contant les règles + * @return le xpath à partir d'un fichier + */ + private String getRules(String fileRules) { + StringBuilder buffer = new StringBuilder(); + + try { + String readInputStream; + + // load core rules + readInputStream = loadRulesFile(getCoreFileRules()); + if (!silent && verbose) { + getLog().info("core rules : " + getCoreFileRules()); + } + buffer.append(readInputStream); + + if (!fileRules.equals(getCoreFileRules())) { + // add custom rules + readInputStream = loadRulesFile(fileRules); + if (!silent && verbose) { + getLog().info("custom rules : " + fileRules); + } + buffer.append(" | ").append(readInputStream); + } + } catch (IOException e) { + throw new ParserException(e); + } + + return buffer.toString(); + } + + private String loadRulesFile(String fileRules) throws IOException { + File f = new File(fileRules); + + InputStream inputStream; + if (f.exists()) { + // load from a file + try { + inputStream = new FileInputStream(f); + } catch (FileNotFoundException e) { + throw new ParserException(e); + } + } else { + // load from classpath + ClassLoader classLoader = getClass().getClassLoader(); + inputStream = classLoader.getResourceAsStream(fileRules); + } + if (inputStream == null) { + throw new ParserException("could not found file of rules : " + fileRules); + } + + inputStream = new BufferedInputStream(inputStream); + + try { + // Lecture + String readInputStream; + readInputStream = readInputStream(inputStream); + return readInputStream; + } catch (IOException e) { + throw new ParserException(e); + } finally { + inputStream.close(); + } + } + + /** + * Permet la lecture d'un InputStream et Suppressions. + * + * @param in le flux entrant + * @return le contenu du flux + * @throws IOException si problème de lecture dans flux entrant + */ + private String readInputStream(InputStream in) throws IOException { + StringBuilder sb = new StringBuilder(); + byte[] buffer = new byte[BUFFER_SIZE]; + while (in.read(buffer, 0, BUFFER_SIZE) != -1) { + String tmp = new String(buffer); + sb.append(tmp); + } + in.close(); + // Suppression + String txt = sb.toString().trim(); + txt = txt.replaceAll("#.*\n", ""); // suppression des commentaires + txt = txt.replaceAll("\\s+", " | "); // contruction du xpath avec des ou + txt = txt.replaceAll("(^ \\| )|( \\| $)", ""); // suppression des ou de début ee fin + return txt; + } +} diff --git a/maven-i18n-plugin/src/main/resources/META-INF/services/org.apache.commons.logging.LogFactory b/maven-i18n-plugin/src/main/resources/META-INF/services/org.apache.commons.logging.LogFactory new file mode 100644 index 0000000..c617f1c --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/META-INF/services/org.apache.commons.logging.LogFactory @@ -0,0 +1 @@ +org.apache.commons.logging.impl.LogFactoryImpl \ No newline at end of file diff --git a/maven-i18n-plugin/src/main/resources/jaxx.rules b/maven-i18n-plugin/src/main/resources/jaxx.rules new file mode 100644 index 0000000..a9a98fd --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/jaxx.rules @@ -0,0 +1,37 @@ +# Règles pour JAXX + +//JDialog/@title +//JFrame/@title + +//tab/@title +//tab/@toolTipText + +//JMenu/@text +//JMenu/@toolTipText + +//JMenuItem/@text +//JMenuItem/@toolTipText + +//JButton/@text +//JButton/@toolTipText + +//JToggleButton/@text +//JToggleButton/@toolTipText + +//JRadioButton/@text +//JRadioButton/@toolTipText + +//JCheckBox/@text +//JCheckBox/@toolTipText + +//JPasswordField/@text +//JPasswordField/@toolTipText + +//JToolBar/@text +//JToolBar/@toolTipText + +//JLabel/@text +//JLabel/@toolTipText + +//JTextField/@text +//JTextField/@toolTipText diff --git a/maven-i18n-plugin/src/main/resources/log4j.properties b/maven-i18n-plugin/src/main/resources/log4j.properties new file mode 100644 index 0000000..4c30f3f --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/log4j.properties @@ -0,0 +1,10 @@ +# Global logging configuration +log4j.rootLogger=ERROR, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n + +# package level +log4j.logger.org.codelutin.util=INFO +log4j.logger.org.codelutin.i18n.plugin=INFO diff --git a/maven-i18n-plugin/src/main/resources/swixat.rules b/maven-i18n-plugin/src/main/resources/swixat.rules new file mode 100644 index 0000000..fe549c9 --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/swixat.rules @@ -0,0 +1,33 @@ +# Règles pour swixat +//Frame/@title +//frame/@title + +//Label/@text +//label/@text + +//Button/@text +//button/@text + +//Menu/@text +//menu/@text + +//Checkbox/@text +//checkbox/@text + +//RadioButton/@text +//radioButton/@text + +//Menuitem/@text +//menuitem/@text + +//Textarea/@text +//textarea/@text + +//Panel/@name +//panel/@name + +//MatrixPanel/@name +//matrixPanel/@name + +//Table/@name +//table/@name diff --git a/maven-i18n-plugin/src/main/resources/validation.rules b/maven-i18n-plugin/src/main/resources/validation.rules new file mode 100644 index 0000000..9f0f8a8 --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/validation.rules @@ -0,0 +1,3 @@ +# Règles pour la validation XWorks + +//validators/field/field-validator/message diff --git a/maven-i18n-plugin/src/main/resources/xwork-validator-1.0.2.dtd b/maven-i18n-plugin/src/main/resources/xwork-validator-1.0.2.dtd new file mode 100644 index 0000000..18ced6c --- /dev/null +++ b/maven-i18n-plugin/src/main/resources/xwork-validator-1.0.2.dtd @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + XWork Validators DTD. + Used the following DOCTYPE. + + <!DOCTYPE validators PUBLIC + "-//OpenSymphony Group//XWork Validator 1.0.2//EN" + "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> +--> + + +<!ELEMENT validators (field|validator)+> + +<!ELEMENT field (field-validator+)> +<!ATTLIST field + name CDATA #REQUIRED +> + +<!ELEMENT field-validator (param*, message)> +<!ATTLIST field-validator + type CDATA #REQUIRED + short-circuit (true|false) "false" +> + +<!ELEMENT validator (param*, message)> +<!ATTLIST validator + type CDATA #REQUIRED + short-circuit (true|false) "false" +> + +<!ELEMENT param (#PCDATA)> +<!ATTLIST param + name CDATA #REQUIRED +> + +<!ELEMENT message (#PCDATA)> +<!ATTLIST message + key CDATA #IMPLIED +> + + diff --git a/maven-i18n-plugin/src/site/apt/index.apt b/maven-i18n-plugin/src/site/apt/index.apt new file mode 100644 index 0000000..794360b --- /dev/null +++ b/maven-i18n-plugin/src/site/apt/index.apt @@ -0,0 +1,42 @@ +---- +Introduction +---- +---- +2009-08-22 +---- + +Maven I18n Plugin + + The Maven I18n Plugin is used to compute i18n bundles for a given maven module. + +Goals Overview + + The I18n plugin has the following goals: + + * {{{parserJava-mojo.html} i18n:parserJava}} parse Java sources. + + * {{{parserJaxx-mojo.html} i18n:parserJaxx}} parse Jaxx sources. + + * {{{parserSwixat-mojo.html} i18n:parserSwixat}} parse Jaxx sources. + + * {{{parserValidation-mojo.html} i18n:parserValidation}} parse XWorks validation sources. + + * {{{parserJavaActionConfig-mojo.html} i18n:parserJavaActionConfig}} parse actions jaxx. + + * {{{parserJavaTabConfig-mojo.html} i18n:parserJavaTabConfig}} parse tabs jaxx. + + * {{{get-mojo.html} i18n:get}} assembly getter computed by parser-like goals. + + * {{{gen-mojo.html} i18n:gen}} generate i18n bundle from getters. + + * {{{bundle-mojo.html} i18n:bundle}} generate unique i18n bundle for final application. + + * {{{help-mojo.html} i18n:help}} display help. + +Usage + + Instructions on how to use the Help Plugin can be found in {{{usages.html} usages}} page. + +Examples + + <to be done.> diff --git a/maven-i18n-plugin/src/site/apt/usages.apt b/maven-i18n-plugin/src/site/apt/usages.apt new file mode 100644 index 0000000..b7e8c6d --- /dev/null +++ b/maven-i18n-plugin/src/site/apt/usages.apt @@ -0,0 +1,8 @@ +---- +Usages +---- +---- +2009-08-22 +---- + + <To be done...> \ No newline at end of file diff --git a/maven-i18n-plugin/src/site/site.xml b/maven-i18n-plugin/src/site/site.xml new file mode 100644 index 0000000..cda222c --- /dev/null +++ b/maven-i18n-plugin/src/site/site.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="${project.url}" /> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Utilisateur"> + <item name="Introduction" href="index.html"/> + <item name="Usages" href="usages.html"/> + <item name="Goals" href="plugin-info.html"> + <item name="parserJava" href="parserJava-mojo.html"/> + <item name="parserJaxx" href="parserJaxx-mojo.html"/> + <item name="parserSwixat" href="parserSwixat-mojo.html"/> + <item name="parserJavaActionConfig" href="parserJavaActionConfig-mojo.html"/> + <item name="parserJavaTabConfig" href="parserJavaTabConfig-mojo.html"/> + <item name="parserXWorks" href="parserValidation-mojo.html"/> + <item name="get" href="get-mojo.html"/> + <item name="gen" href="gen-mojo.html"/> + <item name="bundle" href="bundle-mojo.html"/> + <item name="help" href="help-mojo.html"/> + </item> + </menu> + + <menu name="Téléchargement"> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/nuiton-i18n-api/LICENSE.txt b/nuiton-i18n-api/LICENSE.txt new file mode 100644 index 0000000..3f7b8b1 --- /dev/null +++ b/nuiton-i18n-api/LICENSE.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/nuiton-i18n-api/README.txt b/nuiton-i18n-api/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/nuiton-i18n-api/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/nuiton-i18n-api/changelog.txt b/nuiton-i18n-api/changelog.txt new file mode 100644 index 0000000..d9bea52 --- /dev/null +++ b/nuiton-i18n-api/changelog.txt @@ -0,0 +1,2 @@ +1.0.0 ? + * initial release diff --git a/nuiton-i18n-api/doc/FormatConverter-uml.uxf b/nuiton-i18n-api/doc/FormatConverter-uml.uxf new file mode 100644 index 0000000..78bb4bf --- /dev/null +++ b/nuiton-i18n-api/doc/FormatConverter-uml.uxf @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?><umlet_diagram><element><type>com.umlet.element.base.SequenceDiagram</type><coordinates><x>80</x><y>20</y><w>975</w><h>500</h></coordinates><panel_attributes>_factory:CFactory_|_XMLConverter_|_SQLConverter_|_values:FormatMap_|_App_ +5->4:put(FORMAT_SQL, val) +5->4:5,4:convert(FORMAT_XML) +4->1:4,1:convert(clazz, FORMAT_XML, values) +1->2:1,2:convert(factory, clazz, FORMAT_XML, values) +2->4:2,4:containsKey(FORMAT_SQL) +4.>2:2,4:true +2->4:2,4:convert(factory, FORMAT_SQL) +4.>2:2,4:result +2->2:2:sqlToXML +2.>1:2,1:result +1.>4:1,4:result +4->4:4:put(FORMAT_XML, result) +4.>5:4,5:result +</panel_attributes><additional_attributes></additional_attributes></element><element><type>com.umlet.element.base.Note</type><coordinates><x>400</x><y>0</y><w>340</w><h>20</h></coordinates><panel_attributes>Convertion entre deux format compatible</panel_attributes><additional_attributes></additional_attributes></element><element><type>com.umlet.element.base.Note</type><coordinates><x>380</x><y>530</y><w>340</w><h>20</h></coordinates><panel_attributes>Convertion entre deux format imcompat [...] +5->4:put(FORMAT_YYY, val) +5->4:5,4:convert(FORMAT_XML) +4->1:4,1:convert(clazz, FORMAT_XML, values) +1->2:1,2:convert(factory, clazz, FORMAT_XML, values) +2->4:2,4:containsKey(FORMAT_SQL) +4.>2:2,4:false +2->4:2,4:convert(factory, FORMAT_JAVA) +4->4:4:unconvert(factory, FORMAT_YYY) +4->1:1,4:unconvert(clazz, FORMAT_YYY, values) +1->3:1,3:unconvert(factory, clazz, FORMAT_YYY, values) +3.>1:1,3:result[java] +1.>4:1,4:result[java] +4->4:4:put(FORMAT_JAVA, result) +4.>2:2,4:result[java] +2->2:2:javaToXML +2.>1:2,1:result[xml] +1.>4:1,4:result[xml] +4->4:4:put(FORMAT_XML, result) +4.>5:4,5:result[xml]</panel_attributes><additional_attributes></additional_attributes></element></umlet_diagram> \ No newline at end of file diff --git a/nuiton-i18n-api/pom.xml b/nuiton-i18n-api/pom.xml new file mode 100644 index 0000000..009f1b4 --- /dev/null +++ b/nuiton-i18n-api/pom.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>i18n</artifactId> + <version>1.0.0</version> + </parent> + + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n-api</artifactId> + + <dependencies> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + </dependencies> + + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + + <name>nuiton-i18n - api</name> + <description>Api of i18n system</description> + <inceptionYear>2004</inceptionYear> + + <!-- ************************************************************* --> + <!-- *** Build Settings ****************************************** --> + <!-- ************************************************************* --> + + <packaging>jar</packaging> + + <build> + + <defaultGoal>install</defaultGoal> + + </build> + + <!-- ************************************************************* --> + <!-- *** Build Environment ************************************** --> + <!-- ************************************************************* --> + + <profiles> + <profile> + <id>release-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + + <!-- launch in a release the assembly automaticly --> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>create-assemblies</id> + <phase>verify</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + <configuration> + <attach>false</attach> + <descriptorRefs> + <descriptorRef>deps</descriptorRef> + <descriptorRef>full</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + + </plugins> + + </build> + </profile> + </profiles> + +</project> diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java new file mode 100644 index 0000000..61604c0 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java @@ -0,0 +1,284 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n; + +/** + * Une énumération pour représenter le pays d'une locale + * <p/> + * <a href="http://www.iso.org/iso/country_codes"><code>ISO 3166-1:1998 (ICS n° 01.140.20)</code></a>. + * <p/> + * <a href="http://www.iso.org/iso/french_country_names_and_code_elements">la liste des codes</a> + * <p/> + * Chaque pays est repésenté ainsi : + * <pre>A2, // A3 Number Country name</pre> + * + * @author chemit + */ +public enum CountryEnum { + + AF, // AFG 004 AFGHANISTAN + AL, // ALB 008 ALBANIA + DZ, // DZA 012 ALGERIA + AS, // ASM 016 AMERICAN SAMOA + AD, // AND 020 ANDORRA + AO, // AGO 024 ANGOLA + AI, // AIA 660 ANGUILLA + AQ, // ATA 010 ANTARCTICA + AG, // ATG 028 ANTIGUA AND BARBUDA + AR, // ARG 032 ARGENTINA + AM, // ARM 051 ARMENIA + AW, // ABW 533 ARUBA + AU, // AUS 036 AUSTRALIA + AT, // AUT 040 AUSTRIA + AZ, // AZE 031 AZERBAIJAN + BS, // BHS 044 BAHAMAS + BH, // BHR 048 BAHRAIN + BD, // BGD 050 BANGLADESH + BB, // BRB 052 BARBADOS + BY, // BLR 112 BELARUS + BE, // BEL 056 BELGIUM + BZ, // BLZ 084 BELIZE + BJ, // BEN 204 BENIN + BM, // BMU 060 BERMUDA + BT, // BTN 064 BHUTAN + BO, // BOL 068 BOLIVIA + BA, // BIH 070 BOSNIA AND HERZEGOWINA + BW, // BWA 072 BOTSWANA + BV, // BVT 074 BOUVET ISLAND + BR, // BRA 076 BRAZIL + IO, // IOT 086 BRITISH INDIAN OCEAN TERRITORY + BN, // BRN 096 BRUNEI DARUSSALAM + BG, // BGR 100 BULGARIA + BF, // BFA 854 BURKINA FASO + BI, // BDI 108 BURUNDI + KH, // KHM 116 CAMBODIA + CM, // CMR 120 CAMEROON + CA, // CAN 124 CANADA + CV, // CPV 132 CAPE VERDE + KY, // CYM 136 CAYMAN ISLANDS + CF, // CAF 140 CENTRAL AFRICAN REPUBLIC + TD, // TCD 148 CHAD + CL, // CHL 152 CHILE + CN, // CHN 156 CHINA + CX, // CXR 162 CHRISTMAS ISLAND + CC, // CCK 166 COCOS (KEELING) ISLANDS + CO, // COL 170 COLOMBIA + KM, // COM 174 COMOROS + CG, // COG 178 CONGO + CK, // COK 184 COOK ISLANDS + CR, // CRI 188 COSTA RICA + CI, // CIV 384 COTE D'IVOIRE + HR, // HRV 191 CROATIA (local name: Hrvatska) + CU, // CUB 192 CUBA + CY, // CYP 196 CYPRUS + CZ, // CZE 203 CZECH REPUBLIC + DK, // DNK 208 DENMARK + DJ, // DJI 262 DJIBOUTI + DM, // DMA 212 DOMINICA + DO, // DOM 214 DOMINICAN REPUBLIC + TP, // TMP 626 EAST TIMOR + EC, // ECU 218 ECUADOR + EG, // EGY 818 EGYPT + SV, // SLV 222 EL SALVADOR + GQ, // GNQ 226 EQUATORIAL GUINEA + ER, // ERI 232 ERITREA + EE, // EST 233 ESTONIA + ET, // ETH 210 ETHIOPIA + FK, // FLK 238 FALKLAND ISLANDS (MALVINAS) + FO, // FRO 234 FAROE ISLANDS + FJ, // FJI 242 FIJI + FI, // FIN 246 FINLAND + FR, // FRA 250 FRANCE + FX, // FXX 249 FRANCE, METROPOLITAN + GF, // GUF 254 FRENCH GUIANA + PF, // PYF 258 FRENCH POLYNESIA + TF, // ATF 260 FRENCH SOUTHERN TERRITORIES + GA, // GAB 266 GABON + GM, // GMB 270 GAMBIA + GE, // GEO 268 GEORGIA + DE, // DEU 276 GERMANY + GH, // GHA 288 GHANA + GI, // GIB 292 GIBRALTAR + GR, // GRC 300 GREECE + GL, // GRL 304 GREENLAND + GD, // GRD 308 GRENADA + GP, // GLP 312 GUADELOUPE + GU, // GUM 316 GUAM + GT, // GTM 320 GUATEMALA + GN, // GIN 324 GUINEA + GW, // GNB 624 GUINEA-BISSAU + GY, // GUY 328 GUYANA + HT, // HTI 332 HAITI + HM, // HMD 334 HEARD AND MC DONALD ISLANDS + HN, // HND 340 HONDURAS + HK, // HKG 344 HONG KONG + HU, // HUN 348 HUNGARY + IS, // ISL 352 ICELAND + IN, // IND 356 INDIA + ID, // IDN 360 INDONESIA + IR, // IRN 364 IRAN (ISLAMIC REPUBLIC OF) + IQ, // IRQ 368 IRAQ + IE, // IRL 372 IRELAND + IL, // ISR 376 ISRAEL + IT, // ITA 380 ITALY + JM, // JAM 388 JAMAICA + JP, // JPN 392 JAPAN + JO, // JOR 400 JORDAN + KZ, // KAZ 398 KAZAKHSTAN + KE, // KEN 404 KENYA + KI, // KIR 296 KIRIBATI + KP, // PRK 408 KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF + KR, // KOR 410 KOREA, REPUBLIC OF + KW, // KWT 414 KUWAIT + KG, // KGZ 417 KYRGYZSTAN + LA, // LAO 418 LAO PEOPLE'S DEMOCRATIC REPUBLIC + LV, // LVA 428 LATVIA + LB, // LBN 422 LEBANON + LS, // LSO 426 LESOTHO + LR, // LBR 430 LIBERIA + LY, // LBY 434 LIBYAN ARAB JAMAHIRIYA + LI, // LIE 438 LIECHTENSTEIN + LT, // LTU 440 LITHUANIA + LU, // LUX 442 LUXEMBOURG + MO, // MAC 446 MACAU + MK, // MKD 807 (provis) MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF + MG, // MDG 450 MADAGASCAR + MW, // MWI 454 MALAWI + MY, // MYS 458 MALAYSIA + MV, // MDV 462 MALDIVES + ML, // MLI 466 MALI + MT, // MLT 470 MALTA + MH, // MHL 584 MARSHALL ISLANDS + MQ, // MTQ 474 MARTINIQUE + MR, // MRT 478 MAURITANIA + MU, // MUS 480 MAURITIUS + YT, // MYT 175 MAYOTTE + MX, // MEX 484 MEXICO + FM, // FSM 583 MICRONESIA, FEDERATED STATES OF + MD, // MDA 498 MOLDOVA, REPUBLIC OF + MC, // MCO 492 MONACO + MN, // MNG 496 MONGOLIA + MS, // MSR 500 MONTSERRAT + MA, // MAR 504 MOROCCO + MZ, // MOZ 508 MOZAMBIQUE + MM, // MMR 104 MYANMAR + NA, // NAM 516 NAMIBIA + NR, // NRU 520 NAURU + NP, // NPL 524 NEPAL + NL, // NLD 528 NETHERLANDS + AN, // ANT 530 NETHERLANDS ANTILLES + NC, // NCL 540 NEW CALEDONIA + NZ, // NZL 554 NEW ZEALAND + NI, // NIC 558 NICARAGUA + NE, // NER 562 NIGER + NG, // NGA 566 NIGERIA + NU, // NIU 570 NIUE + NF, // NFK 574 NORFOLK ISLAND + MP, // MNP 580 NORTHERN MARIANA ISLANDS + NO, // NOR 578 NORWAY + OM, // OMN 512 OMAN + PK, // PAK 586 PAKISTAN + PW, // PLW 585 PALAU + PA, // PAN 591 PANAMA + PG, // PNG 598 PAPUA NEW GUINEA + PY, // PRY 600 PARAGUAY + PE, // PER 604 PERU + PH, // PHL 608 PHILIPPINES + PN, // PCN 612 PITCAIRN + PL, // POL 616 POLAND + PT, // PRT 620 PORTUGAL + PR, // PRI 630 PUERTO RICO + QA, // QAT 634 QATAR + RE, // REU 638 REUNION + RO, // ROM 642 ROMANIA + RU, // RUS 643 RUSSIAN FEDERATION + RW, // RWA 646 RWANDA + KN, // KNA 659 SAINT KITTS AND NEVIS + LC, // LCA 662 SAINT LUCIA + VC, // VCT 670 SAINT VINCENT AND THE GRENADINES + WS, // WSM 882 SAMOA + SM, // SMR 674 SAN MARINO + ST, // STP 678 SAO TOME AND PRINCIPE + SA, // SAU 682 SAUDI ARABIA + SN, // SEN 686 SENEGAL + SC, // SYC 690 SEYCHELLES + SL, // SLE 694 SIERRA LEONE + SG, // SGP 702 SINGAPORE + SK, // SVK 703 SLOVAKIA (Slovak Republic) + SI, // SVN 705 SLOVENIA + SB, // SLB 090 SOLOMON ISLANDS + SO, // SOM 706 SOMALIA + ZA, // ZAF 710 SOUTH AFRICA + ES, // ESP 724 SPAIN + LK, // LKA 144 SRI LANKA + SH, // SHN 654 ST. HELENA + PM, // SPM 666 ST. PIERRE AND MIQUELON + SD, // SDN 736 SUDAN + SR, // SUR 740 SURINAME + SJ, // SJM 744 SVALBARD AND JAN MAYEN ISLANDS + SZ, // SWZ 748 SWAZILAND + SE, // SWE 752 SWEDEN + CH, // CHE 756 SWITZERLAND + SY, // SYR 760 SYRIAN ARAB REPUBLIC + TW, // TWN 158 TAIWAN, PROVINCE OF CHINA + TJ, // TJK 762 TAJIKISTAN + TZ, // TZA 834 TANZANIA, UNITED REPUBLIC OF + TH, // THA 764 THAILAND + TG, // TGO 768 TOGO + TK, // TKL 772 TOKELAU + TO, // TON 776 TONGA + TT, // TTO 780 TRINIDAD AND TOBAGO + TN, // TUN 788 TUNISIA + TR, // TUR 792 TURKEY + TM, // TKM 795 TURKMENISTAN + TC, // TCA 796 TURKS AND CAICOS ISLANDS + TV, // TUV 798 TUVALU + UG, // UGA 800 UGANDA + UA, // UKR 804 UKRAINE + AE, // ARE 784 UNITED ARAB EMIRATES + GB, // GBR 826 UNITED KINGDOM + US, // USA 840 UNITED STATES + UM, // UMI 581 UNITED STATES MINOR OUTLYING ISLANDS + UY, // URY 858 URUGUAY + UZ, // UZB 860 UZBEKISTAN + VU, // VUT 548 VANUATU + VA, // VAT 336 VATICAN CITY STATE (HOLY SEE) + VE, // VEN 862 VENEZUELA + VN, // VNM 704 VIET NAM + VG, // VGB 092 VIRGIN ISLANDS (BRITISH) + VI, // VIR 850 VIRGIN ISLANDS (U.S.) + WF, // WLF 876 WALLIS AND FUTUNA ISLANDS + EH, // ESH 732 WESTERN SAHARA + YE, // YEM 887 YEMEN + YU, // YUG 891 YUGOSLAVIA + ZR, // ZAR 180 ZAIRE + ZM, // ZMB 894 ZAMBIA + ZW; // ZWE 716 ZIMBABWE + + public static CountryEnum valueOf(String country, CountryEnum defaultValue) { + CountryEnum countryValue = null; + try { + countryValue = CountryEnum.valueOf(country.toUpperCase()); + } catch (IllegalArgumentException e) { + System.err.println("unfound country " + country + ", will use default one : " + defaultValue); + } catch (NullPointerException e) { + System.err.println("unfound country " + country + ", will use default one : " + defaultValue); + } + return countryValue == null ? defaultValue : countryValue; + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java new file mode 100644 index 0000000..abaf2ce --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nDefaultTooltipFilter.java @@ -0,0 +1,43 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * i18nDefaultTooltipFilter.java + * + * Created: 2 déc. 2003 + * + * @author Benjamin Poussin <poussin@codelutin.com> + * Copyright Code Lutin + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.i18n; + +public class I18nDefaultTooltipFilter implements I18nFilter { // I18nDefaultTooltipFilter + + @Override + public String applyFilter(String message) { + if (message != null && message.startsWith("defaultToolTip-")) { + return null; + } + return message; + } +} // I18nDefaultTooltipFilter + diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java new file mode 100644 index 0000000..725b6ad --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java @@ -0,0 +1,131 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * I18nFileReader.java + * + * Created: Nov 22, 2004 + * + * @author Cédric Pineau <pineau@codelutin.com> + * @version $Revision$ + * + * Last update : $Date$ + * by : $Author$ + */ + +package org.nuiton.i18n; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Properties; +import java.util.regex.Pattern; + +/** Classe assurant la lecture et les possibles traitement nécessaires à I18n. */ +public class I18nFileReader extends Properties { + + protected static final Pattern commentPattern = Pattern.compile("[^\\\\]#"); + + protected static final Pattern splitPattern = Pattern.compile("[^\\\\]="); + private static final long serialVersionUID = 3611718334066783394L; + + public void load(InputStream inStream, String encodingTo) throws IOException { + Charset charsetTo = Charset.forName(encodingTo); + BufferedReader readerFile; + readerFile = new BufferedReader(new InputStreamReader(inStream, charsetTo)); + String lineFile; + StringBuilder builderFile; + builderFile = new StringBuilder(); + while ((lineFile = readerFile.readLine()) != null) { + builderFile.append(lineFile).append('\n'); + } + readerFile.close(); + super.load(new ByteArrayInputStream(builderFile.toString().getBytes())); + } + + protected String interpretBackslashes(String message) { + int backslashIndex = -1; + while ((backslashIndex = message.indexOf("\\", backslashIndex + 1)) != -1) { + if (message.length() >= backslashIndex + 1) { + char charNextToBackslash = message.charAt(backslashIndex + 1); + char replacementChar; + switch (charNextToBackslash) { + case '\\': + replacementChar = '\\'; + break; + case 't': + replacementChar = '\t'; + break; + case 'n': + replacementChar = '\n'; + break; + case ' ': + replacementChar = ' '; + break; + case '=': + replacementChar = '='; + break; + case ':': + replacementChar = ':'; + break; + default: + replacementChar = '\\'; + break; + } + message = message.substring(0, backslashIndex) + replacementChar + message.substring(backslashIndex + 2); + } + } + return message; + } + + private static char[] chars = {'\\', '\n', '\t', ' ', '=', ':'}; + + protected String serializeBackslashes(String message) { + for (char c : chars) { + int charIndex = -1; + while ((charIndex = message.indexOf(c, charIndex + 2)) != -1) { + String replacementString = "" + c; + switch (c) { + case '\\': + replacementString = "\\\\"; + break; + case '\t': + replacementString = "\\t"; + break; + case '\n': + replacementString = "\\n"; + break; + case ' ': + replacementString = "\\ "; + break; + case '=': + replacementString = "\\="; + break; + case ':': + replacementString = "\\:"; + break; + } + message = message.substring(0, charIndex) + replacementString + message.substring(charIndex + 1); + } + } + return message; + } + +} //I18nFileReader diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFilter.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFilter.java new file mode 100644 index 0000000..0bd566d --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFilter.java @@ -0,0 +1,37 @@ +/* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +/* * + * i18nFilter.java + * + * Created: 2 déc. 2003 + * + * @author Benjamin Poussin <poussin@codelutin.com> + * Copyright Code Lutin + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.i18n; + +public interface I18nFilter { // I18nFilter + + public String applyFilter(String message); +} // I18nFilter + diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java new file mode 100644 index 0000000..67d4cd0 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java @@ -0,0 +1,194 @@ +package org.nuiton.i18n; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Logger; +import java.util.zip.ZipFile; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.LocaleConverter; + +/** + * + * @author chemit + */ +public class I18nUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(I18nUtil.class); + public static final String ISO_8859_1_ENCONDING = "ISO-8859-1"; + public static final String UTF_8_ENCONDING = "UTF-8"; + public static final String DEFAULT_ENCODING = ISO_8859_1_ENCONDING; + public static final Locale DEFAULT_LOCALE = Locale.UK; + + /** + * Parse a list of {@link Locale} seperated by comma. + * + * Example : fr_FR,en_GB + * + * @param str the string representation of locale separated by comma + * @return list of available locales + * @throws IllegalArgumentException ia a locale is not valid + */ + public static Locale[] parseLocales(String str) throws IllegalArgumentException { + List<Locale> result = new java.util.ArrayList<Locale>(); + String[] bundlesToUse = str.split(","); + for (int i = 0, j = bundlesToUse.length; i < j; i++) { + String s = bundlesToUse[i].trim(); + // on devrait verifier que le bundle existe + try { + Locale l = (Locale) new LocaleConverter().convert(Locale.class, s); + result.add(l); + } catch (Exception e) { + throw new IllegalArgumentException("bundle " + s + " is not a valid locale,e"); + } + } + return result.toArray(new Locale[result.size()]); + } + + public static Locale newLocale(String str) { + if (str == null) { + // get use locale + return newLocale(null, null); + } + try { + return (Locale) new LocaleConverter().convert(Locale.class, str); + } catch (Exception e) { + Logger.getLogger("org.nuiton.i18n.I18n").warning("could not load locale '" + str + " for reason : " + e.getMessage()); + // use default locale + return DEFAULT_LOCALE; + } + } + + public static Locale newLocale(String language, String country) { + if (language == null) { + // get user locale + language = System.getProperty("user.language", DEFAULT_LOCALE.getLanguage()); + country = System.getProperty("user.country", DEFAULT_LOCALE.getCountry()); + } + return newLocale(language + (country == null ? "" : '_' + country)); + } + + /** + * Test if an url contains the given directory with no recurse seeking. + * + * @param url the url to seek + * @param directory the directory to find + * @return <code>true</code> if directory was found, <code>false</code> otherwise. + * @throws java.io.IOException if any io pb + */ + public static boolean containsDirectDirectory(URL url, String directory) throws IOException { + String fileName = url.getFile(); + // TODO deal with encoding in windows, this is very durty, but it works... + File file = new File(fileName.replaceAll("%20", " ")); + if (!file.exists()) { + return false; + } + if (isJar(fileName) || isZip(fileName)) { + // cas ou le fichier du classLoader est un fichier jar ou zip + if (log.isTraceEnabled()) { + log.trace("zip to search " + file); + } + return new ZipFile(file).getEntry(directory + "/") != null; + } + if (file.isDirectory()) { + // cas ou le ichier du classLoader est un repertoire + if (log.isTraceEnabled()) { + log.trace("directory to search " + file); + } + return new File(file, directory).exists(); + } + + if (log.isWarnEnabled()) { + log.warn("unknown resource type " + url); + } + return false; + } + + /** + * Verifie si le fichier est un fichier jar. + * + * @param name nom du fichier a tester + * @return vrai si le fichier se termine par .jar faux sinon + */ + static public boolean isJar(String name) { + if (name != null && name.length() > 4) { + String ext = name.substring(name.length() - 4, name.length()); + return ".jar".equalsIgnoreCase(ext); + } + return false; + } + + /** + * Verifie si le fichier est un fichier zip + * + * @param name nom du fichier a tester + * @return vrai si le fichier se termine par .zip faux sinon + */ + static public boolean isZip(String name) { + if (name != null && name.length() > 4) { + String ext = name.substring(name.length() - 4, name.length()); + return ".zip".equalsIgnoreCase(ext); + } + return false; + } + + /** + * Retourne la liste des fichiers correspondant au pattern donne, aucun + * ordre ne doit être supposé sur les fichiers. + * + * @param repository repertoire dans lequel on recherche les fichiers + * @param pattern le nom du fichier a extraire du fichier du repertoire doit + * correspondre au pattern (repertoire + nom compris). si le + * pattern est null, tous les fichiers trouvé sont retourné. + * @return la liste des urls correspondant au pattern + */ + static public List<URL> getURLsFromDirectory(File repository, String pattern) { + try { + if (log.isTraceEnabled()) { + log.trace("search '" + pattern + "' in " + repository); + } + + List<URL> urlList = new ArrayList<URL>(); + File[] filesList = repository.listFiles(); + + if (filesList != null) { + + for (File file : filesList) { + + String name = file.getAbsolutePath(); + + if (log.isTraceEnabled()) { + log.trace("directory: " + repository + " name: " + name); + } + + // cas de recursivite : repertoire dans un repertoire + if (file.exists() && file.isDirectory()) { + urlList.addAll(getURLsFromDirectory(file, + pattern)); + // si le fichier du repertoire n'est pas un repertoire + // on verifie s'il correspond au pattern + } else if (pattern == null || name.matches(pattern)) { + URL url = file.toURI().toURL(); + if (log.isTraceEnabled()) { + log.trace("directory: " + repository + " url: " + url); + } + urlList.add(url); + } + } + } + if (log.isTraceEnabled()) { + log.trace("found with pattern '" + pattern + "' : " + urlList); + } + return urlList; + } catch (MalformedURLException eee) { + throw new IllegalArgumentException("Erreur lors de la conversion de l'url " + repository + " (pattern " + pattern + ") " + eee.getMessage(), eee); + //throw new ResourceException("Le fichier n'a pu être converti en URL", eee); + } + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java new file mode 100644 index 0000000..fae1ace --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java @@ -0,0 +1,181 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n; + +/** + * Une énumération pour représenter le langue d'une locale définie dans la norme + * <a href="http://www.iso.org/iso/iso_catalogue/catalogue_ics/catalogue_detail_ics.htm?csnumber=22109&ICS1=1&ICS2=140&ICS3=20"><code>ISO 639-1:1998 (ICS n° 01.140.20)</code></a>. + * <p/> + * <a href="http://www.loc.gov/standards/iso639-2/php/French_list.php">la liste des codes</a> + * + * @author chemit + */ +public enum LanguageEnum { + + aa, // Afar + ab, // Abkhazian + af, // Afrikaans + am, // Amharic + ar, // Arabic + as, // Assamese + ay, // Aymara + az, // Azerbaijani + ba, // Bashkir + be, // Byelorussian + bg, // Bulgarian + bh, // Bihari + bi, // Bislama + bn, // Bengali; Bangla + bo, // Tibetan + br, // Breton + ca, // Catalan + co, // Corsican + cs, // Czech + cy, // Welsh + da, // Danish + de, // German + dz, // Bhutani + el, // Greek + en, // English + eo, // Esperanto + es, // Spanish + et, // Estonian + eu, // Basque + fa, // Persian + fi, // Finnish + fj, // Fiji + fo, // Faroese + fr, // French + fy, // Frisian + ga, // Irish + gd, // Scots Gaelic + gl, // Galician + gn, // Guarani + gu, // Gujarati + ha, // Hausa + he, // Hebrew (formerly iw) + hi, // Hindi + hr, // Croatian + hu, // Hungarian + hy, // Armenian + ia, // Interlingua + id, // Indonesian (formerly in) + ie, // Interlingue + ik, // Inupiak + is, // Icelandic + it, // Italian + iu, // Inuktitut + ja, // Japanese + jw, // Javanese + ka, // Georgian + kk, // Kazakh + kl, // Greenlandic + km, // Cambodian + kn, // Kannada + ko, // Korean + ks, // Kashmiri + ku, // Kurdish + ky, // Kirghiz + la, // Latin + ln, // Lingala + lo, // Laothian + lt, // Lithuanian + lv, // Latvian, Lettish + mg, // Malagasy + mi, // Maori + mk, // Macedonian + ml, // Malayalam + mn, // Mongolian + mo, // Moldavian + mr, // Marathi + ms, // Malay + mt, // Maltese + my, // Burmese + na, // Nauru + ne, // Nepali + nl, // Dutch + no, // Norwegian + oc, // Occitan + om, // (Afan) Oromo + or, // Oriya + pa, // Punjabi + pl, // Polish + ps, // Pashto, Pushto + pt, // Portuguese + qu, // Quechua + rm, // Rhaeto-Romance + rn, // Kirundi + ro, // Romanian + ru, // Russian + rw, // Kinyarwanda + sa, // Sanskrit + sd, // Sindhi + sg, // Sangho + sh, // Serbo-Croatian + si, // Sinhalese + sk, // Slovak + sl, // Slovenian + sm, // Samoan + sn, // Shona + so, // Somali + sq, // Albanian + sr, // Serbian + ss, // Siswati + st, // Sesotho + su, // Sundanese + sv, // Swedish + sw, // Swahili + ta, // Tamil + te, // Telugu + tg, // Tajik + th, // Thai + ti, // Tigrinya + tk, // Turkmen + tl, // Tagalog + tn, // Setswana + to, // Tonga + tr, // Turkish + ts, // Tsonga + tt, // Tatar + tw, // Twi + ug, // Uighur + uk, // Ukrainian + ur, // Urdu + uz, // Uzbek + vi, // Vietnamese + vo, // Volapuk + wo, // Wolof + xh, // Xhosa + yi, // Yiddish (formerly ji) + yo, // Yoruba + za, // Zhuang + zh, // Chinese + zu; // Zulu + + public static LanguageEnum valueOf(String language, LanguageEnum defaultValue) { + LanguageEnum languageValue = null; + try { + languageValue = LanguageEnum.valueOf(language.toLowerCase()); + } catch (IllegalArgumentException e) { + System.err.println("Unfound language " + language + ", will use default one " + defaultValue); + } catch (NullPointerException e) { + System.err.println("Unfound language " + language + ", will use default one " + defaultValue); + } + return languageValue == null ? defaultValue : languageValue; + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java new file mode 100644 index 0000000..556e1e7 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java @@ -0,0 +1,160 @@ +/* +* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import java.util.Iterator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Class to represent a i18n Bundle. + * <p/> + * A bundle is defined by a resource prefix (eg /tmp/bundle.properties), and a list of locale implemented entries. + * <p/> + * The property {@link #bundlePrefix} is the equals order property. + * <p/> + * The property {@link #entries} contains all entries defined for this bundle. + * <p/> + * The method {@link #getEntries(java.util.Locale)} filter entries for a given locale, including scope inclusive property. + * <p/> + * The method {@link #getEntries(I18nBundleScope)} filter entries for a givne scope, with no inclusive logi. + * <p/> + * Thoses filter methods return result in the order defines in {@link I18nBundleEntry}, e.g + * <pre> + * XXX.properties + * XXX-fr.properties + * XXX-fr_FR.properties + * </pre> + * In that way, we can load resource in the good order : load before more general scope to more specialized. + * + * @author chemit + * @see I18nBundleScope + * @see I18nBundleEntry + */ +public class I18nBundle implements Iterable<I18nBundleEntry>{ + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static final Log log = LogFactory.getLog(I18nBundle.class); + + /** les entrés du bundle */ + protected List<I18nBundleEntry> entries; + + /** le nom du bundle encapsulé (correspond au prefix de l'url de chargement) */ + final String bundlePrefix; + + public I18nBundle(String bundlePrefix) { + this.bundlePrefix = bundlePrefix; + } + + public String getBundlePrefix() { + return bundlePrefix; + } + + /** + * Obtain the entries for a given locale, with a inclusive scope search. + * <p/> + * The order of result respect {@link I18nBundleEntry} order. + * + * @param locale the required locale + * @return the array of entries matching extacly the locale or one of the lesser scope one. + */ + public I18nBundleEntry[] getEntries(Locale locale) { + I18nBundleScope scope = I18nBundleScope.valueOf(locale); + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundleEntry entry : entries) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if ((i18nBundleScope == scope || i18nBundleScope.ordinal() < scope.ordinal()) && entry.matchLocale(locale, scope)) { + result.add(entry); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Obtain the entries for a given <code>scope</code> <ith no incluvie logic. + * <p/> + * The order of result respect {@link I18nBundleEntry} order. + * + * @param scope the required scope + * @return the list of entries matching exactly the given scope + */ + public I18nBundleEntry[] getEntries(I18nBundleScope scope) { + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundleEntry entry : entries) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == scope) { + result.add(entry); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** @return number of entries in bundle */ + public int size() { + return entries == null ? 0 : entries.size(); + } + + @Override + public String toString() { + String s = super.toString(); + return "<" + s.substring(s.lastIndexOf(".") + 1) + ", bundlePrefix:" + bundlePrefix + ", size:" + size() + ">"; + } + + protected List<I18nBundleEntry> getEntries() { + return entries; + } + + protected boolean matchLocale(Locale locale) { + I18nBundleScope scope = I18nBundleScope.valueOf(locale); + boolean result = false; + if (size() != 0) { + for (I18nBundleEntry entry : entries) { + if (entry.matchLocale(locale, scope)) { + result = true; + break; + } + } + } + return result; + } + + public boolean addEntry(I18nBundleEntry entry) { + if (entries == null) { + entries = new ArrayList<I18nBundleEntry>(); + } + boolean b = entries.add(entry); + if (log.isDebugEnabled()) { + log.info(this + "\n\t" + entry); + } + return b; + } + + @Override + public Iterator<I18nBundleEntry> iterator() { + return entries.iterator(); + } + + +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java new file mode 100644 index 0000000..744b2eb --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java @@ -0,0 +1,192 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.Properties; +import org.nuiton.i18n.I18nUtil; +import org.nuiton.i18n.I18nFileReader; + +/** + * A class to represent an entry in a bundle. + * <p/> + * The object matches exactly one resource file in a given scope. + * <p/> + * The object has three properties : + * <ul> + * <li> {@link #path} : the path to resource file where to find transaltion for the entry. + * <li> {@link #locale} : the locale of the entry + * <li> {link #scope} ; the scope of the entry + * </ul> + * This object defines a equals order base on property {@link #path}. + * <p/> + * This object is {@link Comparable}, the order relation is defined like this : + * <ul> + * <li> sort first on {@link #scope}, in the scope order (see {@link I18nBundleScope}), + * <li> if scopes are equals, sort on {@link #locale} string representation. + * </ul> + * + * @author chemit + * @see I18nBundleScope + */ +public class I18nBundleEntry implements Comparable<I18nBundleEntry> { + + /** path to resource file */ + protected URL path; + /** local of the entry, can be null if general scope */ + protected Locale locale; + /** scope of the entry */ + protected I18nBundleScope scope; + + /** + * Constructor if an bundle entry. + * <p/> + * It is defined by a <code>path</code> of the resource file, a scope and a locale. + * + * @param path the path of the resource file fo the bundle entry + * @param locale the given locale of the bundle entry + * @param scope the scope of the given entry + */ + public I18nBundleEntry(URL path, Locale locale, I18nBundleScope scope) { + this.path = path; + this.locale = locale; + this.scope = scope; + } + + public URL getPath() { + return path; + } + + public Locale getLocale() { + return locale; + } + + public I18nBundleScope getScope() { + return scope; + } + + /** + * Method to match or not a bundle entry for a given scope and locale. + * <p/> + * We use the inclusive property of scope, means that we accept all entries on the path + * to the generalest entry for a givne locale. + * + * @param locale the locale to match + * @param scope the highest scope to match + * @return <code>true</code> if the entry match the scope and locale + * * + */ + public boolean matchLocale(Locale locale, I18nBundleScope scope) { + if (this.locale == null) { + // a general bundle entry is always matched! + return true; + } + if (locale == null) { + // can not match a specialized entry with a general scope + return false; + } + // match full locale, or at least a language + return this.locale.equals(locale) || + (this.scope.ordinal() < scope.ordinal() && locale.getLanguage().equals(this.locale.getLanguage())); + } + + /** + * For a given language, load the resource file of this entry into the <code>resource</code> + * properties object. + * + * @param resource the save of resources already loaded + * @throws IOException if any pb while reading resource file + */ + public void load(Properties resource) throws IOException { + InputStream inputStream = null; + StringBuilder sb = new StringBuilder(); + try { + I18nFileReader fileReader = new I18nFileReader(); + inputStream = getPath().openStream(); + //String encoding = language.getEncoding(); + if (I18nBundle.log.isDebugEnabled()) { + sb.append(getPath()).append("\n"); + } + // TC 20081117 always use ISO_8859_1_ENCONDING, since java does it like this. + fileReader.load(inputStream, I18nUtil.ISO_8859_1_ENCONDING); + + if (I18nBundle.log.isDebugEnabled()) { + for (Entry<Object, Object> entry : fileReader.entrySet()) { + sb.append(I18nUtil.ISO_8859_1_ENCONDING).append(" : ").append(entry).append("\n"); + } + } + for (Entry<Object, Object> entry : fileReader.entrySet()) { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + if (value.trim().isEmpty()) { + // if there is a previous sentence loaded but not empty + // do not override it + String oldValue = (String) resource.get(key); + if (oldValue != null) { + continue; + } + } + resource.put(key, value); + } + //resource.putAll(fileReader); + if (I18nBundle.log.isDebugEnabled()) { + sb.append("nbSentences : ").append(fileReader.size()).append("\n"); + sb.append("====================================="); + } + fileReader.clear(); + + } finally { + if (I18nBundle.log.isDebugEnabled()) { + I18nBundle.log.debug(sb.toString()); + } + if (inputStream != null) { + inputStream.close(); + } + } + } + + @Override + public int compareTo(I18nBundleEntry o) { + int i = getScope().compareTo(o.getScope()); + if (i == 0) { + // same scope, sort on locale + i = getLocale().toString().compareTo(o.getLocale().toString()); + } + return i; + } + + @Override + public boolean equals(Object o) { + return this == o || o instanceof I18nBundleEntry && path.equals(((I18nBundleEntry) o).path); + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + @Override + public String toString() { + String s = super.toString(); + return "<" + s.substring(s.lastIndexOf(".") + 1) + ", locale:" + locale + ", scope " + scope + ", path:" + path + ">"; + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java new file mode 100644 index 0000000..5cab696 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java @@ -0,0 +1,613 @@ +package org.nuiton.i18n.bundle; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.I18nUtil; + +/** + * Classe qui est responsable de la detection et construction + * de {@link I18nBundle}. + * + * On retrouve aussi ici des méthodes utiles de parcours de bundles. + * + * @author chemit + * + * @since 1.0.6 + */ +public class I18nBundleFactory { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(I18nBundleFactory.class); + /** pattern to find all i18n bundles in classloader class path */ + public static final String SEARCH_BUNDLE_PATTERN = ".*i18n/.+\\.properties"; + public static final String DIRECTORY_SEARCH_BUNDLE_PATTERN = "i18n"; + protected static String UNIQUE_BUNDLE_PATH = "/META-INF/"; + public static String UNIQUE_BUNDLE_DEF = "%1$s-definition.properties"; + public static String UNIQUE_BUNDLE_ENTRY = "%1$s-%2$s.properties"; + public static String BUNDLE_DEF_LOCALES = "locales"; + public static String BUNDLES_FOR_LOCALE = "bundles."; + + /** + * Récuperation de toutes les locales connus par un ensemble de bundles. + * + * @param bundles les bundles a parcourir + * @return la liste des locales rencontrées + */ + public static Locale[] getLocales(I18nBundle... bundles) { + Set<Locale> result = new java.util.HashSet<Locale>(); + for (I18nBundle i18nBundle : bundles) { + for (I18nBundleEntry entry : i18nBundle.getEntries()) { + Locale o = entry.getLocale(); + if (o != null) { + result.add(o); + } + } + } + return result.toArray(new Locale[result.size()]); + } + + /** + * Récuperation des noms de bundle par un ensemble de bundles. + * + * @param bundles les bundles a parcourir + * @return la liste des noms de bundle rencontrées + */ + public static String[] getBundleNames(I18nBundle... bundles) { + List<String> result = new ArrayList<String>(); + for (I18nBundle i18nBundle : bundles) { + result.add(i18nBundle.getBundlePrefix()); + } + return result.toArray(new String[result.size()]); + } + + /** + * Filtrage des bundles qui correspondante à la locale donnée. + * + * @param l la locale à filtrer + * @param bundles les bundles a parcourir + * @return les bundles qui correspondent à la locale donnée. + */ + public static I18nBundle[] getBundles(Locale l, I18nBundle... bundles) { + List<I18nBundle> result = new ArrayList<I18nBundle>(); + for (I18nBundle i18nBundle : bundles) { + if (i18nBundle.matchLocale(l)) { + result.add(i18nBundle); + } + } + return result.toArray(new I18nBundle[result.size()]); + } + + /** + * Récupération de toutes les entrées de bundles pour les bundles données. + * + * @param bundles les bundles a parcourir + * @return toutes les entrées de bundles. + */ + public static I18nBundleEntry[] getBundleEntries(I18nBundle... bundles) { + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundle i18nBundle : bundles) { + List<I18nBundleEntry> list = i18nBundle.getEntries(); + if (!list.isEmpty()) { + result.addAll(list); + } + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Filtrage des entrées de bundles pour une locale donnée. + * + * On essaye de trouver les meilleurs entrées possibles (possibilité de + * promotion). + * + * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à + * charger par le système i18n pour une locale donnée. + * + * Note: Par defaut, on n'effectue pas les promotions générales + * ({@link #getBundleEntries(Locale, Locale, boolean, I18nBundle[])} + * + * @param l la locale à filtrer + * @param defaultLocale la locale à utiliser pour les promotions + * @param bundles les bundles a parcourir + * @return les entrées de bundles filtrés. + */ + public static I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale, I18nBundle... bundles) { + return getBundleEntries(l, defaultLocale, false, bundles); + } + + /** + * Filtrage des entrées de bundles pour une locale donnée. + * + * On essaye de trouver les meilleurs entrées possibles (possibilité de + * promotion). + * + * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à + * charger par le système i18n pour une locale donnée. + * + * @param l la locale à filtrer + * @param defaultLocale la locale à utiliser pour les promotions + * @param promuteGeneral un drapeau pour indiquer si l'on autorise le + * chargement de la locale par defaut si pour un bundle donne on a + * pas trouve de traductions pour la locale donnee. + * @param bundles les bundles a parcourir + * @return les entrées de bundles filtrés. + */ + public static I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale, boolean promuteGeneral, I18nBundle... bundles) { + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + for (I18nBundle i18nBundle : bundles) { + I18nBundleEntry[] entries = i18nBundle.getEntries(l); + if (entries.length == 0) { + //no entry found for the bundle, try pomotion + entries = promuteBundle(i18nBundle, l, defaultLocale, promuteGeneral); + } + result.addAll(Arrays.asList(entries)); + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + /** + * Teste si un ensemble de bundles contient au moins une entrée. + * + * @param bundles les bundles a parcourir + * @return <code>true</code> si aucune entree trouvee, <code>false</code> + * autrement. + */ + public static boolean isEmpty(I18nBundle... bundles) { + for (I18nBundle i18nBundle : bundles) { + if (!i18nBundle.getEntries().isEmpty()) { + // on a trouve au moins une entree + return false; + } + } + return true; + } + + /** + * Recherche la liste des url de toutes les resources i18n, i.e les urls + * des fichiers de traduction en mode uniqueBundleName. + * + * On va d'abord rechercher un fichier /META-INF/unqiueBundleName-definition.properties + * + * Dans ce fichier il y a une entree locales qui contient les locales du bundle + * + * Ensuite pour chaque locale on recupere l'url du fichier : + * + * /META-INF/uniqueBundleName-locale.properties + * + * Exemple : + * + * <code> + * fichier de définition : /META-INF/monAppli-definition.properties + * locales=fr_fr,es_ES + * + * fichiers de traduction + * /META-INF/monAppli-fr_FR.properties + * /META-INF/monAppli-es_ES.properties + * + * </code> + * + * @param uniqueBundleName le nom de l'unique bundle a charger + * @return la liste des urls de bundle i18n + */ + public static URL[] getURLs(String uniqueBundleName) { + + String definitionFileName = String.format(UNIQUE_BUNDLE_DEF, uniqueBundleName); + URL[] urls = null; + + try { + URL defURL = I18nBundleFactory.class.getResource(UNIQUE_BUNDLE_PATH + definitionFileName); + Properties p = loadUniqueNameDefFile(uniqueBundleName); + + String localesAsStr = p.getProperty(BUNDLE_DEF_LOCALES); + Locale[] locales = I18nUtil.parseLocales(localesAsStr); + List<URL> lUrls = new java.util.ArrayList<URL>(1); + String prefixURL = defURL.toString(); + prefixURL = prefixURL.substring(0, prefixURL.length() - definitionFileName.length()); + //FIXME on devrait tester que la resource est disponible ? + + for (Locale l : locales) { + String url = prefixURL + String.format(UNIQUE_BUNDLE_ENTRY, uniqueBundleName, l.toString()); + log.info("detected bundle properties file : " + url); + URL u = new URL(url); +// //FIXME on devrait tester que la resource est disponible ? + + lUrls.add(u); + } + if (!lUrls.isEmpty()) { + urls = lUrls.toArray(new URL[lUrls.size()]); + } else { + // l'unique bundle n'a pas ete trouve! + // on utilise la methode classique de chargement avec recherche + // de tous les bundles i18n + log.warn("not bundle files detected in " + prefixURL); + urls = null; + } + + } catch (Exception ex) { + log.warn("could not load unique bundle " + uniqueBundleName + " for reason " + ex.getMessage(), ex); + urls = null; + + } + return urls; + } + + public static Properties loadUniqueNameDefFile(String uniqueBundleName) { + String definitionFileName = String.format(UNIQUE_BUNDLE_DEF, uniqueBundleName); + Properties p = new Properties(); + try { + URL defURL = I18nBundleFactory.class.getResource(UNIQUE_BUNDLE_PATH + definitionFileName); + log.info("definition i18n file : " + defURL); + InputStream stream = defURL.openStream(); + p.load(stream); + stream.close(); + } catch (Exception ex) { + log.warn("could not load unique bundle " + uniqueBundleName + " for reason " + ex.getMessage(), ex); + } + return p; + } + + /** + * Recherche la liste des url de toutes les resources i18n, i.e les urls + * des fichiers de traduction. + * + * @param urls des urls de resources i18n deja calcule, à ajouter au resultat sans traitement particulier + * @return la liste des urls de bundle i18n + */ + public static URL[] getURLs(URL... urls) { + + try { + // on calcule toutes les urls utilisable dans le classloader donnee + List<URL> urlToSeek = new ArrayList<URL>(); + urlToSeek.addAll(Arrays.asList(urls)); + + // on va maintenant supprimer toutes les urls qui ne respectent pas + // le pattern i18n : il faut que la resource contienne un repertoire i18n + // ce simple test permet de restreindre la recherche des resources + // i18n qui est tres couteuse + int size = urlToSeek.size(); + for (Iterator<URL> it = urlToSeek.iterator(); it.hasNext();) { + URL url = it.next(); + if (!I18nUtil.containsDirectDirectory(url, DIRECTORY_SEARCH_BUNDLE_PATTERN)) { + if (log.isDebugEnabled()) { + log.debug("skip url with no " + DIRECTORY_SEARCH_BUNDLE_PATTERN + " directory : " + url); + } + it.remove(); + } + } + + if (log.isDebugEnabled()) { + log.debug("detect " + urlToSeek.size() + " i18n capable url (out of " + size + ")"); + } + + List<URL> listURLs = new java.util.ArrayList<URL>(); + + for (URL url : urlToSeek) { + // on recherche tous les fichiers de traduction pour cet url + + List<URL> result = null; + + if (log.isDebugEnabled()) { + log.debug("seek in : " + url); + } + + String fileName = url.getFile(); + // TODO deal with encoding in windows, this is very durty, but it + // works... + File file = new File(fileName.replaceAll("%20", " ")); + + if (I18nUtil.isJar(fileName)) { + // cas ou le ichier du classLoader est un fichier jar + if (log.isDebugEnabled()) { + log.debug("jar to search " + file); + } + result = getURLsFromJar(url, file); + + } else if (file.isDirectory()) { + // cas ou le ichier du classLoader est un repertoire + if (log.isDebugEnabled()) { + log.debug("directory to search " + file); + } + // on traite le cas ou il peut y avoir des repertoire dans ce + // repertoire + result = getURLsFromDirectory(url, file); + } + if (result != null && !result.isEmpty()) { + listURLs.addAll(result); + } + + } + return listURLs.toArray(new URL[listURLs.size()]); + } catch (Exception eee) { + log.warn("Unable to find urls for urls : " + urls + " for reason " + eee.getMessage(), eee); + return new URL[0]; + } + } + + /** + * Detecte les bundles i18n a partir des urls des fichiers de traduction + * donnes. + * + * Tous les entrées de bundles sont triees dans l'ordre des scopes i18n. + * + * @param urls les urls des fichiers de traductions + * @return la liste des bundle i18n construits à partir des fichiers de + * traduction donnes. + */ + public static List<I18nBundle> detectBundles(URL... urls) { + + List<String> bundleNames = new ArrayList<String>(); + List<I18nBundle> bundles = new ArrayList<I18nBundle>(); + + for (URL url : urls) { + + if (addBundleEntry(url, I18nBundleScope.FULL, bundleNames, bundles)) { + // found a full bundle + continue; + } + if (addBundleEntry(url, I18nBundleScope.LANGUAGE, bundleNames, bundles)) { + // found a language bundle + continue; + } + // must be a general bundle with no locale defined + addBundleEntry(url, I18nBundleScope.GENERAL, bundleNames, bundles); + } + bundleNames.clear(); + + // once for all, sort entries from general to full + for (I18nBundle bundle : bundles) { + java.util.Collections.sort(bundle.getEntries()); + } + + return bundles; + } + + protected static boolean addBundleEntry(URL url, I18nBundleScope scope, List<String> bundleNames, List<I18nBundle> bundles) { + String path = url.toString(); + Matcher matcher = scope.getMatcher(path); + if (!matcher.matches()) { + // no match at this scope + return false; + } + // create a new bundle entry + I18nBundleEntry entry = new I18nBundleEntry(url, scope.getLocale(matcher), scope); + if (log.isDebugEnabled()) { + log.debug("bundle (" + bundles.size() + ") : " + entry); + } + // get the associated bundle + I18nBundle bundle = addBundle(scope.getBundlePrefix(matcher), bundleNames, bundles); + // add entry to bundle + bundle.addEntry(entry); + return true; + } + + protected static I18nBundle addBundle(String bundleName, List<String> bundleNames, List<I18nBundle> bundles) { + I18nBundle bundle; + int index = bundleNames.indexOf(bundleName); + if (index > -1) { + bundle = bundles.get(index); + } else { + bundle = new I18nBundle(bundleName); + if (log.isDebugEnabled()) { + log.debug("bundle (" + bundles.size() + ") : " + bundle); + } + bundles.add(bundle); + bundleNames.add(bundleName); + } + return bundle; + } + + /** + * Obtain some rescue entries for a given locale. + * <p/> + * Note: <b>Calling this method implies there is no entry matched by the common method + * {@link #getBundleEntries(Locale, Locale, I18nBundle[])} return a empty array. + * + * @param bundle the bundle to promute + * @param l the locale required + * @param defaultLocale the default locale to used for promotion + * @param promuteGeneral a flag to authorize promotion to default locale + * @return the table of entries promuted for the given locale + */ + protected static I18nBundleEntry[] promuteBundle(I18nBundle bundle, Locale l, Locale defaultLocale, boolean promuteGeneral) { + + I18nBundleScope scope = I18nBundleScope.valueOf(l); + + if (log.isDebugEnabled()) { + log.debug('[' + bundle.getBundlePrefix() + "] did not find matching entries for locale " + l + ". Try to detect best entries..."); + } + + if (bundle.size() == 0) { + // there is no entry to take... + log.warn("PROMUTE NO ENTRY FOUND"); + return new I18nBundleEntry[0]; + } + + if (bundle.size() == 1) { + // there is one entry take it,what ever... + I18nBundleEntry entry = bundle.getEntries().get(0); + log.warn("PROMUTE" + l + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + return new I18nBundleEntry[]{entry}; + } + + List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>(); + + switch (scope) { + case FULL: + promuteFull(bundle, l, defaultLocale, result, promuteGeneral); + break; + case LANGUAGE: + promuteLanguage(bundle, l, defaultLocale, result, promuteGeneral); + break; + case GENERAL: + if (promuteGeneral) { + promuteGeneral(bundle, l, defaultLocale, result); + } + break; + } + return result.toArray(new I18nBundleEntry[result.size()]); + } + + protected static void promuteFull(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result, boolean promuteGeneral) { + if (bundle.size() == 0) { + return; + } + // try with a another FULL matching locale ? + for (I18nBundleEntry entry : bundle.getEntries()) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == I18nBundleScope.FULL && + !entry.getLocale().getCountry().equals(locale.getCountry()) && + entry.getLocale().getLanguage().equals(locale.getLanguage())) { + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + result.add(entry); + // we take the first one, this is a resuce!!! + break; + } + } + if (result.isEmpty()) { + // full promotion failed,trylanguage promotion + promuteLanguage(bundle, locale, defaultLocale, result, promuteGeneral); + } + + } + + protected static void promuteLanguage(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result, boolean promuteGeneral) { + if (bundle.size() == 0) { + return; + } + for (I18nBundleEntry entry : bundle.getEntries()) { + I18nBundleScope i18nBundleScope = entry.getScope(); + // load from general to the max scope and always if there is only one bundle entry found + if (i18nBundleScope == I18nBundleScope.FULL && entry.getLocale().getLanguage().equals(locale.getLanguage())) { + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + // we take the first one, this is a resuce!!! + break; + } + } + if (result.isEmpty() && promuteGeneral) { + // language promotion failed,try general promotion + promuteGeneral(bundle, locale, defaultLocale, result); + } + } + + protected static void promuteGeneral(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result) { + if (bundle.size() == 0) { + return; + } + if (bundle.size() == 1) { + // there is one entry take it,what ever... + I18nBundleEntry entry = bundle.getEntries().get(0); + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + return; + } + I18nBundleScope scope = I18nBundleScope.valueOf(defaultLocale); + for (I18nBundleEntry entry : bundle.getEntries(scope)) { + if (entry.getLocale().equals(defaultLocale)) { + // default locale found + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + result.add(entry); + return; + } + } + + // default locale not found, take the first one ? + I18nBundleEntry entry = bundle.getEntries().get(0); + result.add(entry); + log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); + //TODO Should try to load default en_GB from I18nLoader ? + //I18n.DEFAULT_LOCALE.getCountry() + } + + protected static List<URL> getURLsFromJar(URL incomingURL, File jarfile) { + + String pattern = SEARCH_BUNDLE_PATTERN; + try { + + List<URL> result = new ArrayList<URL>(); + InputStream in = new FileInputStream(jarfile); + ZipInputStream zis = new ZipInputStream(in); + ClassLoader cl = new URLClassLoader(new URL[]{incomingURL}, I18nBundleFactory.class.getClassLoader()); + while (zis.available() != 0) { + ZipEntry entry = zis.getNextEntry(); + + if (entry == null) { + break; + } + + String name = entry.getName(); + + if (pattern == null || name.matches(pattern)) { + // on recupere le fichier correspondant au pattern dans le + // classloader + if (log.isDebugEnabled()) { + log.debug(name + " accepted for pattern " + pattern); + } + URL url = cl.getResource(name); + // on ajoute le fichier correspondant au pattern dans la + // liste + result.add(url); + } + } + + return result; + } catch (Exception eee) { + throw new RuntimeException("n'a pas pu trouve la resource dans le jar " + jarfile.getAbsolutePath(), eee); + } + } + + protected static List<URL> getURLsFromDirectory(URL incomingURL, File repository) { + String pattern = SEARCH_BUNDLE_PATTERN; + try { + if (log.isDebugEnabled()) { + log.debug("search '" + pattern + "' in " + repository); + } + + List<URL> urlList = new ArrayList<URL>(); + File[] filesList = repository.listFiles(); + + if (filesList != null) { + + for (File file : filesList) { + + String name = file.getAbsolutePath(); + + // cas de recursivite : repertoire dans un repertoire + if (file.exists() && file.isDirectory()) { + urlList.addAll(I18nUtil.getURLsFromDirectory(file, + pattern)); + // si le fichier du repertoire n'est pas un repertoire + // on verifie s'il correspond au pattern + } else if (pattern == null || name.matches(pattern)) { + URL url = file.toURI().toURL(); + if (log.isDebugEnabled()) { + log.debug("directory: " + repository + " url: " + url); + } + urlList.add(url); + } + } + } + return urlList; + } catch (MalformedURLException eee) { + throw new RuntimeException("n'a pas pu trouve la resource dans le repertoire " + repository.getAbsolutePath(), eee); + } + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java new file mode 100644 index 0000000..924ec05 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java @@ -0,0 +1,144 @@ +/* +* *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ +package org.nuiton.i18n.bundle; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.nuiton.i18n.I18nUtil; + +/** + * The enumaration defines the scope of a bundle entry. + * <p/> + * There is three scope possible: + * <ul> + * <li>{@link #GENERAL} : for a bundle entry with no locale specialized information, eg : <code>bundle.properties</code></li> + * <li>{@link #LANGUAGE} : for a bundle entry with language locale specialized information, eg : <code>bundle-en.properties</code></li> + * <li>{@link #FULL} : for a bundle entry with full locale specialized information, eg : <code>bundle-en_GB.properties</code></li> + * </ul> + * <p/> + * We define a order relation, from general to full scope : + * <p/> + * {@link #GENERAL} < {@link #LANGUAGE} < {@link #FULL} + * <p/> + * Scopes are inclusives, in a search of entries, eg the search of <code>en_GB</code> will include <code>en</code> scope... + * <p/> + * The {@link #patternAll} is the searching pattern of bundle of the scope. + * <p/> + * The method {@link #getMatcher(String)} obtain from the {@link #patternAll} the matcher for a bundle path. + * <p/> + * The method {@link #getLocale(Matcher)} obtain from the {@link #patternAll} matched in a bundle path, the + * corresponding locale. + * <p/> + * The class offer also a static method {@link #valueOf(java.util.Locale)} to obtain the scope of a locale. + * + * @author chemit + */ +public enum I18nBundleScope { + + /** default scope (with no language, nor country information) */ +// GENERAL("(.*18n/.+)\\.properties") { + GENERAL("(.*/.+)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + // no locale for general bundle + return null; + } + }, + + /** language scope (no country information) */ +// LANGUAGE("(.*18n/.+)-(\\w\\w)\\.properties") { + LANGUAGE("(.*/.+)-(\\w\\w)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + Locale result = null; + if (matcher.matches()) { + result = I18nUtil.newLocale(matcher.group(2)); + } + return result; + } + }, + + /** full scope : language + country */ +// FULL("(.*18n/.+)-(\\w\\w_\\w\\w)\\.properties") { + FULL("(.*/.+)-(\\w\\w_\\w\\w)\\.properties") { + @Override + public Locale getLocale(Matcher matcher) { + Locale result = null; + if (matcher.matches()) { + result = I18nUtil.newLocale(matcher.group(2)); + } + return result; + } + }; + + /** pattern used to detect bundle entry */ + private final Pattern patternAll; + + /** + * Obtain the scope of a given <code>locale</code>. + * <p/> + * The given locale can be null, which means {@link I18nBundleScope#GENERAL} scope. + * + * @param locale given locale to convert + * @return the scope of given locale + */ + public static I18nBundleScope valueOf(Locale locale) { + if (locale == null || locale.getLanguage() == null || locale.getLanguage().length() == 0) { + return GENERAL; + } + if (locale.getCountry() == null || locale.getCountry().length() == 0) { + return LANGUAGE; + } + return FULL; + } + + /** + * get a matcher fro the given path for this scope + * + * @param path the path to treate + * @return the bunle detect matcher + */ + public Matcher getMatcher(String path) { + return patternAll.matcher(path); + } + + /** + * get the locale for a given matcher. + * + * @param matcher the scope matcher to use + * @return the locale + */ + public abstract Locale getLocale(Matcher matcher); + + /** + * @param matcher the scope matcher to use + * @return the prefix of the bundle + */ + public String getBundlePrefix(Matcher matcher) { + String result = null; + if (matcher.matches()) { + result = matcher.group(1); + } + return result; + } + + private I18nBundleScope(String patternAll) { + this.patternAll = Pattern.compile(patternAll); + } +} diff --git a/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java b/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java new file mode 100644 index 0000000..2a2e2a8 --- /dev/null +++ b/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java @@ -0,0 +1,126 @@ +/* + * *##% Lutin utilities library + * Copyright (C) 2004 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* */ + +package org.nuiton.util; + +import org.apache.commons.beanutils.ConversionException; +import org.apache.commons.beanutils.Converter; +import static org.apache.commons.logging.LogFactory.getLog; +import org.nuiton.i18n.CountryEnum; +import org.nuiton.i18n.LanguageEnum; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * classe pour convertir une chaine en un objet {@link java.util.Locale}. + * + * @author chemit + */ +public class LocaleConverter implements Converter { + + private static final Pattern FULL_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})_([a-zA-Z]{2})"); + + private static final Pattern MEDIUM_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})"); + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static org.apache.commons.logging.Log log = getLog(LocaleConverter.class); + + public Object convert(Class aClass, Object value) { + if (value == null) { + throw new ConversionException("can not convert null value in " + this + " convertor"); + } + if (isEnabled(aClass)) { + Object result; + if (isEnabled(value.getClass())) { + result = value; + return result; + } + if (value instanceof String) { + result = valueOf(((String) value).trim()); + return result; + } + } + throw new ConversionException("could not find a convertor for type " + aClass.getName() + " and value : " + value); + } + + protected Locale valueOf(String value) { + try { + Locale result = convertFullScope(value); + + if (result == null) { + result = convertMediumScope(value); + } + + if (result == null) { + throw new ConversionException("could not convert locale " + value); + } + + return result; + } catch (Exception e) { + throw new ConversionException("could not convert locale " + value + " for reason " + e.getMessage()); + } + } + + private Locale convertFullScope(String value) { + Matcher m = FULL_SCOPE_PATTERN.matcher(value); + if (m.matches()) { + // found a full scope pattern (language + country) + LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase()); + CountryEnum country = CountryEnum.valueOf(m.group(2).toUpperCase()); + if (language == null || country == null) { + // not safe + throw new ConversionException("could not convert locale " + value); + } + return new Locale(language.name(), country.name()); + } + return null; + } + + private Locale convertMediumScope(String value) { + Matcher m = MEDIUM_SCOPE_PATTERN.matcher(value); + if (m.matches()) { + // found a medium scope pattern (only language) + LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase()); + + if (language == null) { + // not safe + throw new ConversionException("could not convert locale " + value); + } + return new Locale(language.name()); + } + return null; + } + + + public LocaleConverter() { + if (log.isDebugEnabled()) { + log.debug(this); + } + } + + protected boolean isEnabled(Class aClass) { + return aClass == Locale.class; + } + + public Class<?> getType() { + return Locale.class; + } + +} diff --git a/nuiton-i18n-api/src/main/resources/META-INF/services/org.apache.commons.beanutils.Converter b/nuiton-i18n-api/src/main/resources/META-INF/services/org.apache.commons.beanutils.Converter new file mode 100644 index 0000000..890b48c --- /dev/null +++ b/nuiton-i18n-api/src/main/resources/META-INF/services/org.apache.commons.beanutils.Converter @@ -0,0 +1 @@ +org.nuiton.util.LocaleConverter \ No newline at end of file diff --git a/nuiton-i18n-api/src/site/apt/index.apt b/nuiton-i18n-api/src/site/apt/index.apt new file mode 100644 index 0000000..31b94eb --- /dev/null +++ b/nuiton-i18n-api/src/site/apt/index.apt @@ -0,0 +1,20 @@ +---- +Nuiton-i18n-api +---- +---- +2009-08-22 +---- + +Présentation + + Librairie permettant de rendre les programmes Java multilangue de façon + simple. Il utilise la même philosophie que gettext. C'est à dire que chaque + chaîne de caractères devant être traduite sont tagge avec I18n._("..."). + + Ensuite il suffit d'extraire ces chaînes pour les mettres dans un fichier de + propriété, et d'indiquer quel fichier de propriéttés charger au démarrage de + l'application selon la langue souhaitée. + + + <Veuillez consulter la JavaDoc pour de plus ample détails sur les différentes + librairies.> diff --git a/nuiton-i18n-api/src/site/site.xml b/nuiton-i18n-api/src/site/site.xml new file mode 100644 index 0000000..1e0b6e0 --- /dev/null +++ b/nuiton-i18n-api/src/site/site.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="${project.url}" /> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Utilisateur"> + <item name="Accueil" href="index.html"/> + </menu> + + <menu name="Téléchargement"> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item href="${repository.home.url}/org/nuiton/i18n/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java b/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java new file mode 100644 index 0000000..0d340ae --- /dev/null +++ b/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java @@ -0,0 +1,63 @@ +/** + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.bundle; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Locale; + +/** @author chemit */ +public class I18nBunsleScopeTest { + + Locale locale; + I18nBundleScope excepted; + + @Test + public void testFullScope() { + excepted = I18nBundleScope.FULL; + + locale = new Locale("fr", "FR"); + Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale)); + } + + @Test + public void testLanguageScope() { + excepted = I18nBundleScope.LANGUAGE; + + locale = new Locale("fr"); + Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale)); + + locale = new Locale("fr", ""); + Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale)); + } + + @Test + public void testGeneralScope() { + + excepted = I18nBundleScope.GENERAL; + + locale = null; + Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale)); + + locale = new Locale(""); + Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale)); + } + +} diff --git a/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java b/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java new file mode 100644 index 0000000..0f37b53 --- /dev/null +++ b/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java @@ -0,0 +1,124 @@ +/** + * *##% Lutin utilities library + * Copyright (C) 2004 - 2008 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. ##%* + */ +package org.nuiton.util; + +import junit.framework.TestCase; +import org.apache.commons.beanutils.Converter; + +import java.util.Locale; + +/** @author chemit */ +public class LocaleConverterTest extends TestCase { + + String toConvert; + Locale excepted; + Converter converter; + + @Override + protected void setUp() throws Exception { + super.setUp(); + converter = new LocaleConverter(); + } + + public void testConvertFull() throws Exception { + toConvert = "fr_FR"; + excepted = Locale.FRANCE; + assertEquals(toConvert, excepted); + + toConvert = "fr_fr"; + assertEquals(toConvert, excepted); + + toConvert = "FR_fr"; + assertEquals(toConvert, excepted); + + toConvert = "FR_FR"; + assertEquals(toConvert, excepted); + + toConvert = "\n\tFr_fR "; + assertEquals(toConvert, excepted); + + toConvert = "en_GB"; + excepted = Locale.UK; + assertEquals(toConvert, excepted); + + toConvert = "en_US"; + excepted = Locale.US; + assertEquals(toConvert, excepted); + + //TODO Arch, we must also check coherence ! + toConvert = "fr_GB"; + excepted = new Locale("fr","GB"); + assertEquals(toConvert, excepted); + } + + public void testConvertMedium() throws Exception { + toConvert = "fr"; + excepted = new Locale("fr"); + assertEquals(toConvert, excepted); + + toConvert = "fR"; + assertEquals(toConvert, excepted); + + toConvert = "FR"; + assertEquals(toConvert, excepted); + + toConvert = " fR \t"; + assertEquals(toConvert, excepted); + + toConvert = "en"; + excepted = new Locale("en"); + assertEquals(toConvert, excepted); + + toConvert = "es"; + excepted = new Locale("es"); + assertEquals(toConvert, excepted); + + } + + public void testConvertFailed() throws Exception { + + toConvert = null; + assertConvertFailed(toConvert); + + toConvert = ""; + assertConvertFailed(toConvert); + + toConvert = "fr_"; + assertConvertFailed(toConvert); + + toConvert = "_FR"; + assertConvertFailed(toConvert); + + } + + protected void assertEquals(String toConvert, Locale expected) { + Object result = converter.convert(Locale.class, toConvert); + assertEquals(expected, result); + } + + protected void assertConvertFailed(String toConvert) { + try { + converter.convert(Locale.class, toConvert); + fail(); + } catch (Exception e) { + assertTrue(true); + } + + } +} diff --git a/nuiton-i18n-api/src/test/resources/log4j.properties b/nuiton-i18n-api/src/test/resources/log4j.properties new file mode 100644 index 0000000..e2b28c4 --- /dev/null +++ b/nuiton-i18n-api/src/test/resources/log4j.properties @@ -0,0 +1,11 @@ +# Global logging configuration +log4j.rootLogger=ERROR, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n +#log4j.appender.stdout.layout.ConversionPattern=%%c=%c %%C=%C %%d=%d %%F=%F %%l=%l %%L=%L %%m=%m %%M=%M %%p=%p %%r=%r %%t=%t %%x=%x %%X=%X +# package level +log4j.logger.org.codelutin.i18n=INFO +log4j.logger.org.codelutin.util=INFO +log4j.logger.org.codelutin.option=INFO diff --git a/nuiton-i18n-editor/LICENSE.txt b/nuiton-i18n-editor/LICENSE.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/nuiton-i18n-editor/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/nuiton-i18n-editor/README.txt b/nuiton-i18n-editor/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/nuiton-i18n-editor/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/nuiton-i18n-editor/changelog.txt b/nuiton-i18n-editor/changelog.txt new file mode 100644 index 0000000..f0a3600 --- /dev/null +++ b/nuiton-i18n-editor/changelog.txt @@ -0,0 +1,8 @@ +0.3 ?? 2008???? + * 20090205 [chemit] use lutinproject 3.4, improve jnlp making + * 20081205 [chemit] use lutinproject 3.2, make minimal site + * 20081118 [chemit] use lutinproject 3.1, produce javadoc + * 20081117 [chemit] bump jaxx to 0.6 + * use src/main/java to store jaxx, no more use of uimodel directory + * Update groupId to org.codelutin + * Use org.codelutin:lutinproject:3.0 super-pom \ No newline at end of file diff --git a/nuiton-i18n-editor/pom.xml b/nuiton-i18n-editor/pom.xml new file mode 100644 index 0000000..a8fd2a8 --- /dev/null +++ b/nuiton-i18n-editor/pom.xml @@ -0,0 +1,384 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + <parent> + <groupId>org.nuiton</groupId> + <artifactId>i18n</artifactId> + <version>1.0.0-rc-5-SNAPSHOT</version> + </parent> + + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n-editor</artifactId> + + <dependencies> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>nuiton-i18n-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + <version>${lutinutil.version}</version> + </dependency> + + <!--Jaxx--> + <dependency> + <groupId>org.nuiton.jaxx</groupId> + <artifactId>jaxx-runtime-swing</artifactId> + <version>${jaxx.version}</version> + </dependency> + + <dependency> + <groupId>org.nuiton.jaxx</groupId> + <artifactId>jaxx-runtime-swing-widget</artifactId> + <version>${jaxx.version}</version> + </dependency> + + </dependencies> + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + + <name>Nuiton I18n Editor</name> + + <description>Editeur de fichier i18n</description> + <inceptionYear>2008</inceptionYear> + + <licenses> + <license> + <name>GPL</name> + <url>http://www.gnu.org/licenses/gpl.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + + <!-- ************************************************************* --> + <!-- *** Build Settings ****************************************** --> + <!-- ************************************************************* --> + <packaging>jar</packaging> + + <properties> + + <!-- main class in jar --> + <maven.jar.main.class>org.nuiton.i18n.editor.I18nEditor</maven.jar.main.class> + + <jaxx.version>1.7.1-SNAPSHOT</jaxx.version> + <lutinutil.version>1.1.0-rc-9-SNAPSHOT</lutinutil.version> + + <!-- default license to use --> + <license.licenseName>gpl_v3</license.licenseName> + + <!-- jnlp --> + <!--keystorepath>${codelutin.keystorepath}</keystorepath> + <keystorealias>CodeLutin</keystorealias> + <keystorepass>codelutin</keystorepass> + <jnlp.build.directory>${project.build.directory}/jnlp</jnlp.build.directory> + <jnlpCodebase>${project.url}</jnlpCodebase--> + <!-- to test jnlp file locally --> + <!--jnlpCodebase>file://${jnlp.build.directory}</jnlpCodebase--> + + <!-- jaxx --> + <jaxx.useUIManagerForIcon>true</jaxx.useUIManagerForIcon> + <jaxx.addProjectClassPath>true</jaxx.addProjectClassPath> + <jaxx.addSourcesToClassPath>true</jaxx.addSourcesToClassPath> + </properties> + + <build> + + <resources> + <resource> + <directory>src/main/filters</directory> + <filtering>true</filtering> + <includes> + <include>nuiton-i18n-editor.properties</include> + </includes> + </resource> + <resource> + <directory>src/main/resources</directory> + <includes> + <include>**/*</include> + </includes> + </resource> + <!--resource> + <directory>${jaxx.helpTarget}</directory> + <includes> + <include>**/*</include> + </includes> + </resource--> + </resources> + + <plugins> + + <plugin> + <groupId>org.nuiton.jaxx</groupId> + <artifactId>maven-jaxx-plugin</artifactId> + <version>${jaxx.version}</version> + <executions> + <execution> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.nuiton</groupId> + <artifactId>maven-helper-plugin</artifactId> + <configuration> + <copyToMETA_INF>true</copyToMETA_INF> + </configuration> + <executions> + <execution> + <id>attach-licenses</id> + <goals> + <goal>add-license</goal> + <goal>add-third-party</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- plugin i18n --> + <plugin> + <groupId>${project.groupId}</groupId> + <artifactId>maven-i18n-plugin</artifactId> + <version>${project.version}</version> + <configuration> + <silent>true</silent> + <entries> + <entry> + <basedir>${maven.gen.dir}/java/</basedir> + </entry> + </entries> + </configuration> + <executions> + <execution> + <goals> + <goal>parserJava</goal> + <goal>bundle</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <configuration> + <silent>true</silent> + </configuration> + <executions> + <execution> + <id>copy-dependencies</id> + </execution> + </executions> + + </plugin> + + </plugins> + + <pluginManagement> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <addClasspath>true</addClasspath> + <classpathPrefix>./lib/</classpathPrefix> + </manifest> + </archive> + </configuration> + </plugin> + + <!-- plugin site --> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <version>2.0.1</version> + <dependencies> + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>doxia-module-jrst</artifactId> + <version>1.0.2-rc-1</version> + </dependency> + </dependencies> + </plugin> + + </plugins> + </pluginManagement> + </build> + + <!-- ************************************************************* --> + <!-- *** Build Environment ************************************** --> + <!-- ************************************************************* --> + + <profiles> + <profile> + <id>release-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + <!-- Ajout des libs signe par Sun dans un fichier jnlp separe --> + <!--plugin> + + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <-execution> + <id>JnlpSun</id> + <phase>verify</phase> + <configuration> + <tasks> + <mkdir dir="${jnlp.build.directory}"/> + <copy file="${project.basedir}/src/main/jnlp/sun.jnlp" + verbose="${maven.verbose}" + todir="${jnlp.build.directory}" failonerror="false"> + <filterset> + <filter token="lib" value="javahelp-2.0.02.jar"/> + <filter token="url" value="${jnlpCodebase}"/> + </filterset> + </copy> + <copy file="${project.basedir}/src/main/jnlp/jxlayer.jnlp" + verbose="${maven.verbose}" + todir="${jnlp.build.directory}" failonerror="false"> + <filterset> + <filter token="lib" value="jxlayer-3.0.1.jar"/> + <filter token="url" value="${jnlpCodebase}"/> + </filterset> + </copy> + <copy file="${project.build.directory}/lib/javahelp-2.0.02.jar" + verbose="${maven.verbose}" todir="${jnlp.build.directory}/lib" + failonerror="false"/> + <copy file="${project.build.directory}/lib/jxlayer-3.0.1.jar" + verbose="${maven.verbose}" + todir="${jnlp.build.directory}/lib" failonerror="false"/> + </tasks> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + + <execution> + <id>JnlpToSite</id> + <phase>pre-site</phase> + <configuration> + <tasks> + <mkdir dir="${maven.site.gen.dir}/resources"/> + <copy todir="${maven.site.gen.dir}/resources" verbose="true" + failonerror="false" overwrite="false"> + <fileset dir="${jnlp.build.directory}"> + <include name="**"/> + </fileset> + <fileset dir="target"> + <include name="${project.build.finalName}-bin.zip"/> + </fileset> + + </copy> + </tasks> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin--> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <descriptors> + <descriptor>src/main/assembly/bin.xml</descriptor> + </descriptors> + </configuration> + <executions> + <execution> + <phase>verify</phase> + <goals> + <goal>attached</goal> + </goals> + </execution> + </executions> + </plugin> + + <!--plugin> + <groupId>org.nuiton.thirdparty</groupId> + <artifactId>webstart-maven-plugin</artifactId> + <version>1.0-alpha-2-cl_20090204</version> + <executions> + <execution> + <phase>verify</phase> + <goals> + <goal>jnlp-inline</goal> + </goals> + </execution> + </executions> + <configuration> + <force>false</force> + <dependencies> + <excludes> + <exclude>javax.help:javahelp</exclude> + <exclude>org.swinglabs:jxlayer</exclude> + </excludes> + </dependencies> + <libPath>lib</libPath> + <extensions> + <sun>sun.jnlp</sun> + <jxlayer>jxlayer.jnlp</jxlayer> + </extensions> + <jnlp> + <outputFile>launch-${project.artifactId}.jnlp</outputFile> + <mainClass>${maven.jar.main.class}</mainClass> + <allPermissions>true</allPermissions> + <offlineAllowed>true</offlineAllowed> + </jnlp> + + <sign> + <keystore>${keystorepath}</keystore> + <keypass/> + <storepass>${keystorepass}</storepass> + <storetype/> + <alias>${keystorealias}</alias> + <validity/> + <dnameCn/> + <dnameOu/> + <dnameO/> + <dnameL/> + <dnameSt/> + <dnameC/> + <verify>true</verify> + <keystoreConfig> + <delete>false</delete> + <gen>false</gen> + </keystoreConfig> + </sign> + + + <pack200>false</pack200> + <gzip>true</gzip> + <verbose>false</verbose> + </configuration> + </plugin--> + </plugins> + </build> + </profile> + </profiles> + +</project> diff --git a/nuiton-i18n-editor/src/main/assembly/bin.xml b/nuiton-i18n-editor/src/main/assembly/bin.xml new file mode 100644 index 0000000..a39b7f4 --- /dev/null +++ b/nuiton-i18n-editor/src/main/assembly/bin.xml @@ -0,0 +1,53 @@ +<assembly> + <id>bin</id> + <formats> + <format>zip</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + + <fileSet> + <directory>target/lib</directory> + <outputDirectory>lutini18nEditor/lib</outputDirectory> + <includes> + <include>*.jar</include> + </includes> + <excludes> + <exclude>junit-4.5.jar</exclude> + </excludes> + </fileSet> + + <fileSet> + <directory>target</directory> + <outputDirectory>lutini18nEditor</outputDirectory> + <includes> + <include>*.jar</include> + </includes> + <excludes> + <exclude>*-sources.jar</exclude> + <exclude>*-javadoc.jar</exclude> + </excludes> + </fileSet> + <fileSet> + <outputDirectory>lutini18nEditor</outputDirectory> + + <includes> + <include>README*</include> + <include>LICENSE*</include> + </includes> + </fileSet> + + <fileSet> + <directory>src/main/assembly</directory> + <filtered>true</filtered> + <outputDirectory>lutini18nEditor</outputDirectory> + <fileMode>0755</fileMode> + <includes> + <include>go.sh</include> + <include>go.bat</include> + </includes> + </fileSet> + + </fileSets> +</assembly> \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/assembly/go.bat b/nuiton-i18n-editor/src/main/assembly/go.bat new file mode 100644 index 0000000..cffa0cc --- /dev/null +++ b/nuiton-i18n-editor/src/main/assembly/go.bat @@ -0,0 +1 @@ +java -Xmx512M -Xms512M -jar ${project.build.finalName}.jar %1 %2 %3 %4 %5 %6 %7 %8 %9 \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/assembly/go.sh b/nuiton-i18n-editor/src/main/assembly/go.sh new file mode 100644 index 0000000..5557dd2 --- /dev/null +++ b/nuiton-i18n-editor/src/main/assembly/go.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +MX=512M +java -Xmx$MX -Xms$MX -jar ${project.build.finalName}.jar "$@" \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/filters/nuiton-i18n-editor.properties b/nuiton-i18n-editor/src/main/filters/nuiton-i18n-editor.properties new file mode 100644 index 0000000..eb45262 --- /dev/null +++ b/nuiton-i18n-editor/src/main/filters/nuiton-i18n-editor.properties @@ -0,0 +1,78 @@ +application.name=${project.name} +application.version=${project.version} +application.site.url=${project.url} +application.organisation.name=${project.organization.name} +application.organisation.url=${project.organization.url} + + +# icones d'action (icon.action.nomAction=*.???) +icon.action.about=action-about.png +icon.action.accept=action-accept.png +icon.action.add=action-add.png +icon.action.cancel=action-cancel.png +icon.action.closeTab=action-closeTab.png +icon.action.config=action-config.png +icon.action.edit=action-edit.png +icon.action.open=action-open.png +icon.action.save=action-save.png +icon.action.close=action-close.png +icon.action.delete=action-delete.png +icon.action.exit=action-exit.png +icon.action.error=error.png +icon.action.fullscreen=action-fullscreen.png +icon.action.fileChooser=action-fileChooser.png +icon.action.go-back=action-go-back.png +icon.action.i18n-es=action-i18n-es.png +icon.action.i18n-fr=action-i18n-fr.png +icon.action.i18n-gb=action-i18n-gb.png +icon.action.collapseAll=action-collapseAll.png +icon.action.expandAll=action-expandAll.png + +icon.action.site=action-site.png + +icon.action.show-help=action-show-help.png +icon.action.help=action-help.png +icon.action.leave-fullscreen=action-leave-fullscreen.png + +icon.action.connected=action-connected.png +icon.action.connect_creating=action-connect_creating.png +icon.action.connect_untested=action-connect_untested.png +icon.action.connect_no=action-connect_no.png +icon.action.connect_ok=action-connect_ok.png +icon.action.db-change=action-db-change.png +icon.action.db-local=action-db-local.png +icon.action.db-none=action-db-none.png +icon.action.db-remote=action-db-remote.png +icon.action.go-detail=action-go-detail.png +icon.action.go-down=action-go-down.png +icon.action.go-jump=action-go-jump.png +icon.action.next-step=action-next-step.png +icon.action.importGPS=action-import-gps.png +icon.action.previous-step=action-previous-step.png +icon.action.go-up=action-go-up.png +icon.action.information=action-information.png +icon.action.local-export=action-local-import.png +icon.action.local-import=action-local-export.png +icon.action.remote-export=action-remote-export.png +icon.action.remote-import=action-remote-import.png +icon.action.revert=action-revert.png +icon.action.select-ssl-cert=action-select-ssl-cert.png +icon.action.start=action-synchro-start.png +icon.action.stop=action-synchro-stop.png + +icon.action.wizard-state-pending=action-wizard-state-pending-16.png +icon.action.wizard-state-running=action-wizard-state-running-16.png +icon.action.wizard-state-need_fix=action-wizard-state-need_fix-16.png +icon.action.wizard-state-successed=action-wizard-state-successed-16.png +icon.action.wizard-state-failed=action-wizard-state-failed-16.png +icon.action.wizard-state-canceled=action-wizard-state-canceled-16.png + +icon.action.wizard-start=action-wizard-start-16.png +icon.action.wizard-next=action-wizard-next-16.png +icon.action.wizard-previous=action-wizard-previous-16.png +icon.action.wizard-pause=action-wizard-pause-16.png +icon.action.wizard-refresh=action-wizard-refresh-16.png + +icon.action.wizard-config=action-wizard-config-16.png +icon.action.wizard-message=action-wizard-message-16.png + diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditor.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditor.java new file mode 100644 index 0000000..eda2c96 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditor.java @@ -0,0 +1,201 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor; + +import org.nuiton.i18n.editor.ui.I18nEditorUIHandler; +import org.nuiton.i18n.I18n; + +import java.io.IOException; +import javax.swing.SwingUtilities; +import jaxx.runtime.JAXXContextEntryDef; +import jaxx.runtime.SwingUtil; +import jaxx.runtime.Util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.editor.ui.I18nEditorUI; +import org.nuiton.util.StringUtil; + +import static org.nuiton.i18n.I18n._; + +/** + * L'application. + * + * @author chemit + */ +public class I18nEditor { + + private static long startingTime = System.nanoTime(); + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static Log log = LogFactory.getLog(I18nEditor.class); + + /** + * Lancement de l'interface + * + * @param args path to manage + * @throws java.io.IOException if any IO exception + */ + public static void main(String[] args) throws IOException { + + startingTime = System.nanoTime(); + log.info("I18nEditor start at " + new java.util.Date() + " args: " + java.util.Arrays.toString(args)); + + try { + + I18nEditorContext context = init(args); + + log.info(_("i18neditor.init.context.done", StringUtil.convertTime(startingTime, System.nanoTime()))); + + Util.checkJAXXContextEntry(context, JAXXContextEntryDef.newDef(I18nEditorConfig.class)); + + I18nEditorConfig config = context.getContextValue(I18nEditorConfig.class); + + config.doAction(I18nEditorConfig.Step.AfterInit.ordinal()); + + if (config.isDisplayMainUI()) { + + I18nEditorUIHandler uiHandler = context.getContextValue(I18nEditorUIHandler.class); + + //uiHandler.openProject(context); + + final I18nEditorUI ui = uiHandler.initUI(context, config.isFullScreen()); + + log.info(_("i18neditor.init.ui.done")); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + ui.setVisible(true); + } + }); + + } else { + System.exit(0); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + System.exit(1); + } + + } + + public static I18nEditorContext init(String... args) throws Exception { + // to enable javassist on webstart, must remove any securityManager, + // see if this can be dangerous (should not be since jnlp is signed ?) + // moreover it speeds up the loading :) + System.setSecurityManager(null); + + long t0 = System.nanoTime(); + + //I18n.setUniqueBundleName("nuiton-i18n-editor-i18n"); + + Runtime.getRuntime().addShutdownHook(new Thread("shutdown observe") { + + @Override + public void run() { + try { + super.run(); + + I18nEditorContext.get().close(); + + // force to kill main thread + + log.info(_("i18neditor.init.closed", new java.util.Date())); + Runtime.getRuntime().halt(0); + } catch (Exception ex) { + log.error("error while closing " + ex.getMessage(), ex); + Runtime.getRuntime().halt(1); + } + } + }); + + // init root context + I18nEditorContext context = I18nEditorContext.get(); + + // init config + I18nEditorConfig config = context.getConfig(); + config.parse(args); + + // init i18n + I18n.init(config.getLocale()); + + log.info(_("i18neditor.message.config.loaded", config.getVersion())); + + // prepare ui look&feel and load ui properties + try { + SwingUtil.initNimbusLoookAndFeel(); + } catch (Exception e) { + // could not find nimbus look-and-feel + log.warn(_("i18neditor.warning.nimbus.landf")); + } catch (Throwable e) { + log.warn(_("i18neditor.warning.no.ui"), e); + // pas d'environnement d'ui + config.setCanUseUI(false); + } + + if (config.isCanUseUI()) { + // chargement de la configuration des uis + SwingUtil.loadUIConfig(I18nEditorConfig.APPLICATION_PROPERTIES, null); + } + + if (log.isDebugEnabled()) { + log.debug("init done in " + (StringUtil.convertTime(t0, System.nanoTime()))); + } + return context; + } + + public void help() { + I18nEditorContext context = I18nEditorContext.get(); + I18nEditorConfig config = context.getConfig(); + System.out.println(I18n._("i18neditor.message.help.usage", config.getVersion())); + System.out.println("Options (set with --option <key> <value>:"); + for (I18nEditorConfig.Option o : I18nEditorConfig.Option.values()) { + System.out.println("\t" + o.key + "(" + o.defaultValue + "):" + o.description); + } + + System.out.println("Actions:"); + for (I18nEditorConfig.Action a : I18nEditorConfig.Action.values()) { + System.out.println("\t" + java.util.Arrays.toString(a.aliases) + "(" + a.action + "):" + a.description); + } + disableMainUI(); + } + + public void configure() { + if (log.isDebugEnabled()) { + log.debug(this); + } + I18nEditorContext context = I18nEditorContext.get(); + I18nEditorConfig config = context.getConfig(); + I18nEditorUIHandler handler = context.getContextValue(I18nEditorUIHandler.class); + handler.showConfig(context); + config.setDisplayMainUI(false); + } + + /** + * Désactiver la possiblite de lancer l'ui principale. + */ + public void disableMainUI() { + if (log.isDebugEnabled()) { + log.debug(this); + } + I18nEditorContext context = I18nEditorContext.get(); + I18nEditorConfig config = context.getConfig(); + config.setDisplayMainUI(false); + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorConfig.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorConfig.java new file mode 100644 index 0000000..e953c92 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorConfig.java @@ -0,0 +1,340 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import static org.nuiton.i18n.I18n._; +import org.nuiton.util.FileUtil; +import org.nuiton.util.Version; + +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FilenameFilter; +import java.util.HashSet; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; +import org.nuiton.util.VersionUtil; + +/** + * La configuration de l'application. + * + * Note : cette classe sera auto-instanciée par le contexte applicatif. + * + * TODO ajouter d'autres propriete pour controler quel storage est utilise + * et quel est son type (local ou pas). + * + * @author chemit + * @see jaxx.runtime.DefaultApplicationContext.AutoLoad + */ +@jaxx.runtime.DefaultApplicationContext.AutoLoad +public class I18nEditorConfig extends org.nuiton.util.ApplicationConfig { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(I18nEditorConfig.class); + /** + * le fichier de configuration de l'application avec les informations sur + * le projet (version, license,...) et la configuration des ui (icons, ...) + */ + public static final String APPLICATION_PROPERTIES = "/nuiton-i18n-editor.properties"; + /** + * le nom du repertoire ou sont les donnees de l'application + */ + public static final String USER_DIRECTORY_FILENAME = ".nuiton-i18n-editor"; + /** + * le repertoire utilisateur de l'application + */ + protected static File userDirectory; + + public static File getUserDirectory() { + if (userDirectory == null) { + userDirectory = new File(new File(I18nEditorConfig.getUserHome()), USER_DIRECTORY_FILENAME); + } + return userDirectory; + } + /** + * un drapeau pour savoir si on est en mode pleine écran + */ + protected boolean fullscreen; + /** + * un drepeau pour savoir s'il faut lancer l'interface graphique. + * Cette valeur peut être programmées lors des actions. + */ + private boolean displayMainUI = true; + /** + * drapeau pour savoir si on peut utiliser des ui dans l'environnement. + * + * Par defaut, on suppose qu'on peut utiliser l'environnement graphique + * et si on désactive explicitement ou si pas d'environnement graphique trouvé. + */ + private boolean canUseUI = true; + /** + * la liste des projets déjà connus (dans le repertoire getProjectFile()) + */ + protected Set<String> projects = new HashSet<String>(); + + public I18nEditorConfig() { + + setConfigFileName(Option.CONFIG_FILE.defaultValue); + + // chargement de la configuration interne + + InputStream stream = getClass().getResourceAsStream(APPLICATION_PROPERTIES); + + Properties p = new Properties(); + try { + p.load(stream); + for (Object k : p.keySet()) { + String key = k + ""; + Object value = p.get(k); + if (log.isDebugEnabled()) { + log.debug("install properties " + k + " : " + value); + } + setDefaultOption(key, "" + value); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + for (Option o : Option.values()) { + setDefaultOption(o.key, o.defaultValue); + } + + // on supprime le stamp de snapshot s'il existe + String sVersion = VersionUtil.removeSnapshot(getOption("application.version")); + Version version = VersionUtil.valueOf(sVersion); + setDefaultOption("version", version.getVersion()); + + + // initialisation des répertoires + //FileUtil.setCurrentDirectory(getUserDirectory()); + getUserDirectory().mkdirs(); + // suppression du contenu du répertoire temporaire + FileUtil.deleteRecursively(getTmpDirectory()); + + // initialisation des actions + for (Action a : Action.values()) { + for (String alias : a.aliases) { + addActionAlias(alias, a.action); + } + } + // recuperation de la liste des projets deja connus + String[] list = getProjectsDirectory().list(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".i18nproject"); + } + }); + for (String name : list) { + projects.add(name.substring(0, name.length() - ".i18nproject".length())); + } + } + + public String getCopyrightText() { + return "Version " + getVersion() + " Codelutin @ 2008-2009"; + } + + /** + * @return la version de l'application. + */ + public Version getVersion() { + Version option = getOption(Version.class, "version"); + return option; + } + + public boolean isFullScreen() { + Boolean result = getOptionAsBoolean(Option.FULL_SCREEN.key); + return result != null && result; + } + + public File getTmpDirectory() { + File result = getOptionAsFile(Option.TMP_DIRECTORY.key); + if (!result.exists()) { + result.mkdirs(); + } + return result; + } + + public File getProjectsDirectory() { + File result = getOptionAsFile(Option.PROJECTS_DIRECTORY.key); + if (!result.exists()) { + result.mkdirs(); + } + return result; + } + + public boolean isDisplayMainUI() { + return displayMainUI; + } + + public boolean isCanUseUI() { + return canUseUI; + } + + public Locale getLocale() { + Locale result = getOption(Locale.class, Option.LOCALE.key); + return result; + } + + public Set<String> getProjects() { + return projects; + } + + public void setFullscreen(boolean fullscreen) { + boolean oldValue = isFullScreen(); + setOption(Option.FULL_SCREEN.key, fullscreen + ""); + saveForUser(); + firePropertyChange("fullscreen", oldValue, fullscreen); + } + + public void setDisplayMainUI(boolean b) { + displayMainUI = b; + } + + public void setCanUseUI(boolean canUseUI) { + this.canUseUI = canUseUI; + if (!canUseUI) { + // on ne pourra pas lancer l'ui principale + setDisplayMainUI(false); + } + } + + public void setLocale(Locale newLocale) { + setOption(Option.LOCALE.key, newLocale.toString()); + Locale.setDefault(newLocale); + saveForUser(); + firePropertyChange("locale", null, newLocale); + } + + /** + * Save configuration, in user home directory using the + * {@link #getConfigFileName}. Default, env and commande line note saved + */ + public void saveForUser() { + super.saveForUser(); + } + public static final String[] DEFAULT_JAXX_PCS = { + "locale", "fullScreen" + }; + + public void removeJaxxPropertyChangeListener() { + PropertyChangeListener[] toRemove = jaxx.runtime.Util.findJaxxPropertyChangeListener(DEFAULT_JAXX_PCS, getPropertyChangeListeners()); + if (toRemove == null || toRemove.length == 0) { + return; + } + if (log.isDebugEnabled()) { + log.debug("before remove : " + getPropertyChangeListeners().length); + log.debug("toRemove : " + toRemove.length); + } + for (PropertyChangeListener listener : toRemove) { + removePropertyChangeListener(listener); + } + if (log.isDebugEnabled()) { + log.debug("after remove : " + getPropertyChangeListeners().length); + } + } + + public File getProjectStore(String name) { + return new File(getProjectsDirectory(), name + ".i18nproject"); + } + + ////////////////////////////////////////////////// + // Toutes les options disponibles + ////////////////////////////////////////////////// + public static enum Option implements OptionDef { + + CONFIG_FILE(CONFIG_FILE_NAME, _("i18neditor.config.configFileName.description"), "nuitoni18neditor-config", String.class, true, true), + // directories + TMP_DIRECTORY("tmp.directory", _("i18neditor.config.defaultTmpDirectory.description"), getUserDirectory() + File.separator + "tmp", File.class, false, false), + PROJECTS_DIRECTORY("projects.directory", _("i18neditor.config.defaultProjectsDirectory.description"), getUserDirectory() + File.separator + "projects", File.class, false, false), + // ui config + FULL_SCREEN("ui.fullscreen", _("i18neditor.config.ui.fullscreen"), "false", Boolean.class, false, false), + LOCALE("ui.locale", _("i18neditor.config.ui.locale"), Locale.FRANCE.toString(), Locale.class, false, false); + public final String key; + public final String description; + public final String defaultValue; + public final Class<?> type; + public final boolean _transient; + public final boolean _final; + + private Option(String key, String description, String defaultValue, Class<?> type, boolean _transient, boolean _final) { + this.key = key; + this.description = description; + this.defaultValue = defaultValue; + this.type = type; + this._final = _final; + this._transient = _transient; + } + + @Override + public boolean isFinal() { + return _final; + } + + @Override + public boolean isTransient() { + return _transient; + } + + @Override + public String getDefaultValue() { + return defaultValue; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getKey() { + return key; + } + + @Override + public Class<?> getType() { + return type; + } + } + + public static enum Step { + + AfterInit, BeforeExit + } + + public static enum Action { + + HELP(_("i18neditor.action.commandline.help"), I18nEditor.class.getName() + "#help", "-h", "--help"), + CONFIGURE(_("i18neditor.action.commandline.configure"), I18nEditor.class.getName() + "#configure", "-c", "--configure"), + NO_MAIN_UI(_("i18neditor.action.commandline.disable.main.ui"), I18nEditor.class.getName() + "#disableMainUI", "-n", "--no-main"); + public String description; + public String action; + public String[] aliases; + + private Action(String description, String action, String... aliases) { + this.description = description; + this.action = action; + this.aliases = aliases; + } + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorContext.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorContext.java new file mode 100644 index 0000000..08e319e --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/I18nEditorContext.java @@ -0,0 +1,343 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor; + +import java.beans.PropertyChangeListener; +import java.util.List; +import java.util.Locale; +import jaxx.runtime.JAXXContextEntryDef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.ui.TreeModelMode; +import org.nuiton.i18n.editor.ui.I18nEditorUI; + +/** + * Le contexte de l'application. + * <p/> + * On définit ici toutes les entrées du contexte. + * <p/> + * Note : cette classe possède une instance partagée accéssible via la méthode {@link #get()}. + * + * @author chemit + */ +public class I18nEditorContext extends jaxx.runtime.DefaultApplicationContext { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private final Log log = LogFactory.getLog(I18nEditorContext.class); + //------------------------------------------- + // UI instances + //------------------------------------------- + public static final JAXXContextEntryDef<I18nEditorUI> MAIN_UI_ENTRY_DEF = JAXXContextEntryDef.newDef("mainui", I18nEditorUI.class); + public static final JAXXContextEntryDef<I18nProject> PROJECT_DEF = JAXXContextEntryDef.newDef(I18nProject.class); + private static final JAXXContextEntryDef<String> SELECTED_KEY_DEF = JAXXContextEntryDef.newDef("selectedKey", String.class); + private static final JAXXContextEntryDef<String> RE_SELECTED_KEY_DEF = JAXXContextEntryDef.newDef("reSelectedKey", String.class); + private static final JAXXContextEntryDef<TreeModelMode> TREE_MODEL_MODE_DEF = JAXXContextEntryDef.newDef(TreeModelMode.class); + public static final String PROJECT_PROPERTY = "project"; + public static final String SELECTED_PACKAGES_PROPERTY = "selectedPackages"; + public static final String SELECTED_BUNDLES_PROPERTY = "selectedBundles"; + public static final String SELECTED_KEY_PROPERTY = "selectedKey"; + public static final String TREE_MODEL_MODE_PROPERTY = "treeModelMode"; + /** Séparateur des propriétés */ + public static final String SEPARATOR = "."; + /** + * l'intance partagée accessible après un appel à la méthode + * {@link #init()} + */ + protected static I18nEditorContext instance; + + /** + * Récupération du contexte applicatif. + * + * @return l'instance partagé du contexte. + * @throws IllegalStateException si le contexte n'a pas été initialisé via + * la méthode {@link #init()} + */ + public static synchronized I18nEditorContext get() throws IllegalStateException { + if (instance == null) { + instance = new I18nEditorContext(); + } + return instance; + } + /** + * Un objet pour bloquer le context + */ + protected final Object lock = new Object(); + + protected I18nEditorContext() { + super(); + TREE_MODEL_MODE_DEF.setContextValue(this, TreeModelMode.TREE); + } + + public I18nEditorConfig getConfig() { + return getContextValue(I18nEditorConfig.class); + } + + public I18nProject getProject() { + return PROJECT_DEF.getContextValue(this); + } + + public I18nEditorUI getMainUI() { + return MAIN_UI_ENTRY_DEF.getContextValue(this); + } + + public void addSelectedPackage(String newPackage) { + List<String> list = getProject().getSelectedPackages(); + boolean added = list.add(newPackage); + if (added) { + firePropertyChange(SELECTED_PACKAGES_PROPERTY, null, list); + } + } + + public void removeSelectedPackage(String newPackage) { + List<String> list = getProject().getSelectedPackages(); + boolean removed = list.remove(newPackage); + if (removed) { + firePropertyChange(SELECTED_PACKAGES_PROPERTY, null, list); + } + } + + public void addSelectedBundle(Locale newBundle) { + List<Locale> list = getProject().getSelectedBundles(); + boolean added = list.add(newBundle); + if (added) { + firePropertyChange(SELECTED_BUNDLES_PROPERTY, null, list); + } + } + + public void removeSelectedBundle(Locale newBundle) { + List<Locale> list = getProject().getSelectedBundles(); + boolean removed = list.remove(newBundle); + if (removed) { + firePropertyChange(SELECTED_BUNDLES_PROPERTY, null, list); + } + } + + public TreeModelMode getTreeModelMode() { + return TREE_MODEL_MODE_DEF.getContextValue(this); + } + + public void setTreeModelMode(TreeModelMode mode) { + TreeModelMode oldMode = getTreeModelMode(); + if (mode == null) { + TREE_MODEL_MODE_DEF.removeContextValue(this); + + } else { + TREE_MODEL_MODE_DEF.setContextValue(this, mode); + } + if (log.isDebugEnabled()) { + log.debug("new mode " + mode + " - old mode " + oldMode); + } + firePropertyChange(TREE_MODEL_MODE_PROPERTY, oldMode, mode); + } + + public String getSelectedKey() { + return SELECTED_KEY_DEF.getContextValue(this); + } + + public void setSelectedKey(String key) { + + if (key == null) { + SELECTED_KEY_DEF.removeContextValue(this); + } else { + SELECTED_KEY_DEF.setContextValue(this, key); + } + if (log.isDebugEnabled()) { + log.debug("new selected key : " + key); + } + firePropertyChange(SELECTED_KEY_PROPERTY, null, key); + } + + public void saveSelectedKey() { + String key = getSelectedKey(); + if (log.isDebugEnabled()) { + log.debug("store key to reselect : " + key); + } + if (key == null) { + RE_SELECTED_KEY_DEF.removeContextValue(this); + } else { + RE_SELECTED_KEY_DEF.setContextValue(this, key); + } + SELECTED_KEY_DEF.removeContextValue(this); + } + + public String popSelectedKey() { + String key = RE_SELECTED_KEY_DEF.getContextValue(this); + if (key != null) { + // on supprime la valeur du context + RE_SELECTED_KEY_DEF.removeContextValue(this); + } + if (log.isDebugEnabled()) { + log.debug("pop key to reselect : " + key); + } + return key; + } + + public void lock() throws InterruptedException { + synchronized (lock) { + lock.wait(); + } + } + + public void releaseLock() { + synchronized (lock) { + lock.notify(); + } + } + + /** + * close the application's context. + * + * @throws java.lang.Exception if any pb while closing + */ + public void close() throws Exception { + if (log.isDebugEnabled()) { + log.debug("closing context " + this); + } + + // fermeture du context principal + clear(); + + if (log.isDebugEnabled()) { + log.debug("context closed" + this); + } + } + +// public synchronized void setProject(I18nProject project) throws IOException { +// +// project.load(); +// +// setContextValue(project); +// firePropertyChange("project", null, project); +// firePropertyChange(BUNDLES_I18N_PROPERTY, null, project.getSelectedLocales()); +// firePropertyChange(PACKAGES_I18N_PROPERTY, null, project.getSelectedPackages()); +// } +// /** +// * Récupération d'un bundle utilisateur, créé si il n'existe pas +// * +// * @param bundleName nom du bundle +// * @return bundle +// */ +// public I18nFileReader getUserBundle(String bundleName) { +// String name = REP_NAME + PROJECT_NAME + "-" + bundleName + ".properties"; +// I18nFileReader bundle = getBundles().get(name); +// if (bundle == null) { +// bundle = new I18nFileReader(); +// getUserBundles().put(name, bundle); +// getBundles().put(name, bundle); +// } +// return bundle; +// } +// /** +// * Récupération des valeurs pour les bundles pour une clé +// * +// * @param locales liste des bundles +// * @param key clé +// * @return liste des valeurs pour les bundles pour une clé +// */ +// public List<String> getValues(List<Locale> locales, String key) { +// List<String> values = new ArrayList<String>(); +// +// I18nProject project = getProject(); +// if (project != null) { +// +// for (Locale locale : locales) { +// +// String value = null; +// +// project.getValue(value, locale, key); +// +// values.add(value); +// } +// } +// return values; +// } + /** + * Création d'un nouveau bundle utilistateur + * + * @param bundleName nom du bundle (ex : fr_FR) + */ + public void createBundle(String bundleName) { +// getUserBundle(bundleName); + } + + public void createPackage(String bundleName) { +// getUserBundle(bundleName); + } + + /** + * Enregistrement d'une modification de clé dans le bundle utilisateur associé + * + * @param bundleName nom du bundle + * @param key clé + * @param value nouvelle valeur + */ + public void store(Locale bundleName, String key, String value) { + I18nProject project = getProject(); + if (project != null) { +// project.updateValue(bundleName, bundleName, key, value); + } +// I18nFileReader bundle = getUserBundle(bundleName); +// bundle.put(key, value); + } + + /** + * Enregistrement des bundles utilisateur sur le disque + */ + public void store() { +// Map<String, I18nFileReader> userBundles = getUserBundles(); +// for (String name : userBundles.keySet()) { +// I18nFileReader bundle = userBundles.get(name); +// try { +// bundle.store(new FileWriter(name), null); +// } catch (IOException e) { +// throw new RuntimeException(e); //TODO: Gestion des exceptions +// } +// } + } + + @Override + public void firePropertyChange(String name, Object oldValue, Object newValue) { + super.firePropertyChange(name, oldValue, newValue); + } + public static final String[] DEFAULT_JAXX_PCS = { + SELECTED_PACKAGES_PROPERTY, + SELECTED_BUNDLES_PROPERTY, + PROJECT_PROPERTY, + SELECTED_KEY_PROPERTY, + TREE_MODEL_MODE_PROPERTY + }; + + public void removeJaxxPropertyChangeListener() { + PropertyChangeListener[] toRemove = jaxx.runtime.Util.findJaxxPropertyChangeListener(DEFAULT_JAXX_PCS, getPropertyChangeListeners()); + if (toRemove == null || toRemove.length == 0) { + return; + } + if (log.isDebugEnabled()) { + log.debug("before remove : " + getPropertyChangeListeners().length); + log.debug("toRemove : " + toRemove.length); + } + for (PropertyChangeListener listener : toRemove) { + removePropertyChangeListener(listener); + } + if (log.isDebugEnabled()) { + log.debug("after remove : " + getPropertyChangeListeners().length); + } + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProject.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProject.java new file mode 100644 index 0000000..c44efae --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProject.java @@ -0,0 +1,425 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.bundle.I18nBundle; +import org.nuiton.i18n.bundle.I18nBundleEntry; +import org.nuiton.i18n.bundle.I18nBundleFactory; + +/** + * + * @author chemit + */ +public class AbstractI18nProject implements I18nProject { + + public static final String NAME_PROPERTY_NAME = "name"; + public static final String URLS_PROPERTY_NAME = "urls"; + public static final String STORE_PROJECT_PROPERTY_NAME = "storeProject"; + public static final String STORE_FILE_PROPERTY_NAME = "storeFile"; + public static final Locale[] EMPTY_LOCALE_ARRAY = new Locale[0]; + public static final String EMPTY_STRING = ""; + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + public static final Log log = LogFactory.getLog(AbstractI18nProject.class); + /** le nom du projet */ + protected String name; + /** les urls des fichiers de traduction a charger */ + protected List<URL> urls; + /** les definitions de bundles du projet */ + protected I18nBundle[] i18nBundles; + /** le fichier où conserver le projet (si null, on ne persiste pas le projet) */ + protected File storeFile = new File(""); + /** un drapeau pour savoir si on persiste le projet */ + protected boolean storeProject; + /** un drapeau pour savoir si le modele a ete charge */ + boolean loaded; + /** Les locales selectionnees dans ce projet */ + protected List<Locale> selectedBundles; + /** les paquetages selectionnes dans ce projet */ + protected List<String> selectedPackages; + /** le dictionnaire des fichiers de traductions charges */ + protected Map<String, Properties> resources; + /** pour propager les changements du modele */ + protected PropertyChangeSupport pcs; + + protected AbstractI18nProject() { + this.resources = new LinkedHashMap<String, Properties>(); + this.selectedBundles = new ArrayList<Locale>(); + this.selectedPackages = new ArrayList<String>(); + this.pcs = new PropertyChangeSupport(this); + } + + @Override + public String getName() { + return name; + } + + @Override + public List<URL> getUrls() { + return urls; + } + + @Override + public boolean isStoreProject() { + return storeProject; + } + + @Override + public File getStoreFile() { + return storeFile; + } + + @Override + public void setName(String name) { + String oldValue = this.name; + this.name = name; + firePropertyChange(NAME_PROPERTY_NAME, oldValue, name); + } + + @Override + public void setUrls(List<URL> urls) { + this.urls = urls; + } + + @Override + public void setStoreProject(boolean storeProject) { + boolean oldValue = this.storeProject; + this.storeProject = storeProject; + firePropertyChange(STORE_PROJECT_PROPERTY_NAME, oldValue, storeProject); + } + + @Override + public void setStoreFile(File storeFile) { + File oldValue = this.storeFile; + this.storeFile = storeFile; + firePropertyChange(STORE_FILE_PROPERTY_NAME, oldValue, storeFile); + } + + @Override + public String[] getPackages() { + return I18nBundleFactory.getBundleNames(i18nBundles); + } + + @Override + public Locale[] getBundles() { + return I18nBundleFactory.getLocales(i18nBundles); + } + + @Override + public List<String> getSelectedPackages() { + return selectedPackages; + } + + @Override + public List<Locale> getSelectedBundles() { + return selectedBundles; + } + + @Override + public String[] getKeys() { + if (!loaded) { + return EMPTY_STRING_ARRAY; + } + + List<String> paquetages = getSelectedPackages(); + List<Locale> locales = getSelectedBundles(); + Set<String> keys = new HashSet<String>(); + + for (I18nBundle bundle : i18nBundles) { + if (paquetages.contains(bundle.getBundlePrefix())) { + for (I18nBundleEntry entry : bundle) { + if (!locales.contains(entry.getLocale())) { + continue; + } + Set<Object> keySet = resources.get(entry.getPath().toString()).keySet(); + for (Object object : keySet) { + keys.add(object.toString()); + } + } + } + } + List<String> r = new ArrayList<String>(keys); + return r.toArray(new String[r.size()]); + } + + @Override + public void deleteKey(String packageName, Locale locale, String key) { + checkLoaded(); + Properties resource = getResource(packageName, locale); + if (resource != null) { + resource.remove(key); + } + } + + @Override + public Map<Locale, String> getValues(String packageName, String key) { + if (!loaded) { + return null; + } + + List<String> paquetages = getSelectedPackages(); + + if (!paquetages.contains(packageName)) { + return null; + } + + Map<Locale, String> r = new LinkedHashMap<Locale, String>(); + + List<Locale> locales = getSelectedBundles(); + + I18nBundle bundle = getI18nBundle(packageName); + + for (I18nBundleEntry entry : bundle) { + Locale locale = entry.getLocale(); + if (!locales.contains(locale)) { + continue; + } + Properties resource = resources.get(entry.getPath().toString()); + if (resource.containsKey(key)) { + r.put(locale, resource.getProperty(key).toString()); + } + } + return r; + } + + @Override + public String getValue(String packageName, Locale locale, String key) { + if (!loaded) { + return EMPTY_STRING; + } + Properties resource = getResource(packageName, locale); + if (resource != null) { + Object object = resource.get(key); + return object.toString(); + } + return null; + + } + + @Override + public void addValue(String packageName, Locale locale, String key, String value) { + checkLoaded(); + Properties resource = getResource(packageName, locale); + if (resource != null) { + resource.put(key, value); + } + } + + @Override + public void updateValue(String packageName, Locale locale, String key, String value) { + checkLoaded(); + Properties resource = getResource(packageName, locale); + if (resource != null) { + resource.put(key, value); + } + } + + @Override + public void copyDefinitionTo(I18nProject project) { + // check we are on the same type of project + if (project.getClass() != getClass()) { + throw new IllegalArgumentException("copyDefinitionTo method must use same project type, but was not! (required : " + getClass() + ", but find " + project.getClass() + 3); + } + project.setName(name); + project.setUrls(urls); + project.setStoreProject(storeProject); + // always clean storeFile (could be init in project from ui, but only keep it if necessary) + project.setStoreFile(storeProject ? storeFile : null); + } + + @Override + public void load() throws IOException { + try { + synchronized (this) { + resources.clear(); + selectedBundles.clear(); + selectedPackages.clear(); + + // detections des bundles i18n a partir des urls donnees + List<I18nBundle> tmp = I18nBundleFactory.detectBundles(urls.toArray(new URL[urls.size()])); + i18nBundles = tmp.toArray(new I18nBundle[tmp.size()]); + + selectedBundles.addAll(Arrays.asList(getBundles())); + selectedPackages.addAll(Arrays.asList(getPackages())); + + for (I18nBundle bundle : i18nBundles) { + for (I18nBundleEntry entry : bundle) { + URL path = entry.getPath(); + Properties properties = new Properties(); + entry.load(properties); + resources.put(path.toString(), properties); + } + } + } + } finally { + loaded = true; + } + } + + @Override + public void store() throws IOException { + checkLoaded(); + } + + @Override + public void saveDefinition() throws IOException { + Properties p = new Properties(); + fillDefinition(p); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(getStoreFile()); + p.store(stream, "generated by " + getClass().getName()); + } finally { + if (stream != null) { + stream.close(); + } + } + + } + + @Override + public void fillDefinition(Properties p) { + p.setProperty("name", name); + p.setProperty("class", getClass().getName()); + StringBuilder buffer = new StringBuilder(); + for (URL u : urls) { + buffer.append(',').append(u.toString()); + } + p.setProperty("urls", buffer.substring(1)); + } + + @Override + public String toString() { + return super.toString() + "<name: " + name + ">"; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + if (log.isDebugEnabled()) { + log.debug(this + " / " + listener); + } + pcs.addPropertyChangeListener(listener); + } + + @Override + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + if (log.isDebugEnabled()) { + log.debug(this + " / " + propertyName + " : " + listener); + } + pcs.addPropertyChangeListener(propertyName, listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + if (log.isDebugEnabled()) { + log.debug(this + " / " + listener); + } + pcs.removePropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + if (log.isDebugEnabled()) { + log.debug(this + " / " + propertyName + " : " + listener); + } + pcs.removePropertyChangeListener(propertyName, listener); + } + + @Override + public void removePropertyChangeListeners() { + for (PropertyChangeListener l : pcs.getPropertyChangeListeners()) { + pcs.removePropertyChangeListener(l); + } + } + + @Override + public void fireAllProperties() { + firePropertyChange(NAME_PROPERTY_NAME, null, name); + firePropertyChange(URLS_PROPERTY_NAME, null, urls); + firePropertyChange(STORE_PROJECT_PROPERTY_NAME, null, storeProject); + firePropertyChange(STORE_FILE_PROPERTY_NAME, null, storeFile); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + i18nBundles = null; + resources.clear(); + selectedBundles.clear(); + selectedPackages.clear(); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + protected I18nBundle getI18nBundle(String packageName) { + if (i18nBundles != null) { + for (I18nBundle b : i18nBundles) { + if (b.getBundlePrefix().equals(packageName)) { + return b; + } + } + } + return null; + } + + protected I18nBundleEntry getI18nBundleEntry(String packageName, Locale locale) { + I18nBundle bundle = getI18nBundle(packageName); + if (bundle != null) { + for (I18nBundleEntry entry : bundle) { + if (locale.equals(entry.getLocale())) { + return entry; + } + } + } + return null; + } + + protected Properties getResource(String packageName, Locale locale) { + I18nBundleEntry entry = getI18nBundleEntry(packageName, locale); + if (entry != null) { + Properties resource = resources.get(entry.getPath().toString()); + return resource; + } + return null; + } + + protected void checkLoaded() throws IllegalStateException { + if (!loaded) { + throw new IllegalStateException("project was not loaded"); + } + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProjectProvider.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProjectProvider.java new file mode 100644 index 0000000..989f0dd --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/AbstractI18nProjectProvider.java @@ -0,0 +1,107 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.nuiton.i18n.editor.project; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.editor.project.impl.DirectoryI18nProject; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; + +/** + * + * @param <P> le type de projet supporte. + * + * @author chemit + */ +public abstract class AbstractI18nProjectProvider<P extends I18nProject> implements I18nProjectProvider<P> { + + public static final String CLASS_PARAMETER = "class"; + protected final Class<P> type; + protected final String label; + protected final String description; + protected Map<String, Class<?>> authorizedParameters; + + protected abstract Map<String, Class<?>> initAuthorizedParameters(); + + public AbstractI18nProjectProvider(Class<P> type, String label, String description) { + this.type = type; + this.label = label; + this.description = description; + } + + @Override + public Class<P> getType() { + return type; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getDescription() { + return I18n._(description); + } + + @Override + public Map<String, Class<?>> getAuthorizedParameters() { + if (authorizedParameters == null) { + this.authorizedParameters = Collections.unmodifiableMap(initAuthorizedParameters()); + } + return authorizedParameters; + } + + @Override + public P newProject(ProjectUIModel model) { + I18nProject project = model.getProject(); + P p = newProject(); + project.copyDefinitionTo(p); + return p; + } + + public <T extends Object> T getParameter(Class<T> returnType, String propertyName, Map<String, Object> parameters, boolean mustExist) { + Map<String, Class<?>> map = getAuthorizedParameters(); + // check name + if (!map.containsKey(propertyName)) { + throw new IllegalArgumentException("the property " + propertyName + " is unknown for this project.\nKnown parameters :" + map.keySet()); + } + // check type + Class<?> requiredType = map.get(propertyName); + if (returnType != requiredType && !returnType.isAssignableFrom(requiredType)) { + throw new IllegalArgumentException("the property " + propertyName + " must be of the type " + requiredType + ", but was : " + requiredType); + } + + for (Entry<String, Object> e : parameters.entrySet()) { + if (propertyName.equals(e.getKey())) { + return (T) e.getValue(); + } + } + if (mustExist) { + throw new IllegalArgumentException("could not find property " + propertyName + ".\nKnown properties in :\n" + parameters.keySet()); + } + return null; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProject.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProject.java new file mode 100644 index 0000000..a94ba57 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProject.java @@ -0,0 +1,198 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project; + +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +/** + * + * @author chemit + */ +public interface I18nProject { + + ///////////////////////////////////////////////////////////////////// + // definition accessor ////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + /** + * @return le nom du projet + */ + String getName(); + + /** + * @return les urls du projet + */ + List<URL> getUrls(); + + /** + * + * @return <code>true</code> si la definition du projet est persiste + */ + boolean isStoreProject(); + + /** + * + * @return le chemin du fichier ou persister la definition du projet + * + * Note : n'est pris en compte uniquement si {@link #isStoreProject()} est + * vrai. + */ + File getStoreFile(); + + ///////////////////////////////////////////////////////////////////// + // definition mutator ////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + void setName(String name); + + void setUrls(List<URL> urls); + + void setStoreProject(boolean storeProject); + + void setStoreFile(File storeFile); + + ///////////////////////////////////////////////////////////////////// + // runtime accessor ///////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + /** + * @return les paquetages du projet + */ + String[] getPackages(); + + /** + * @return les locales du projet + */ + Locale[] getBundles(); + + /** + * @return les paquetages selectionnes + */ + List<String> getSelectedPackages(); + + /** + * @return les locales selectionnees + */ + List<Locale> getSelectedBundles(); + + /** + * @return les clefs de traductions pour les packages selectionnee et les locales selectionnes + */ + String[] getKeys(); + + /** + * @param packageName + * @param key la clef de traductions + * @return les traductions pour les packages, locales et clef selectionnees + */ + Map<Locale, String> getValues(String packageName, String key); + + /** + * @param key la clef de traductions + * @param locale la locale + * @param packageName le nom du package + * @return les traductions + */ + String getValue(String packageName, Locale locale, String key); + + /** + * Copy roject definition to the given project. + * + * @param project the destination project + */ + void copyDefinitionTo(I18nProject project); + + /** + * Supprime une traduction pour une clef donnee dans une langue et un paquetage donne. + * + * @param packageName le paquetage ou supprimer la clef + * @param locale la locale de la traduction + * @param key la clef de traduction + */ + void deleteKey(String packageName, Locale locale, String key); + + /** + * Ajouter une traduction dans le projet. + * + * @param packageName le paquetage ou ajouter la clef + * @param locale la locale de la traduction + * @param key la clef de traduction + * @param value la traduction + */ + void addValue(String packageName, Locale locale, String key, String value); + + /** + * Mettre a jour une traduction dans le projet. + * + * @param packageName le paquetage ou ajouter la clef + * @param locale la locale de la traduction + * @param key la clef de traduction + * @param value la traduction + */ + void updateValue(String packageName, Locale locale, String key, String value); + + /** + * Chargement des traductions du projet. + * + * @throws IOException + */ + void load() throws IOException; + + /** + * Enregistrement des traductions du projet. + * + * @throws IOException + */ + void store() throws IOException; + + /** + * Sauvegarde la definition du projet. + * + * @throws IOException pour tout pb lors de la sauvegarde + */ + void saveDefinition() throws IOException; + + /** + * Remplit un dictionnaire avec les proprietes de la definition du projet. + * + * @param p le dictionnaire des proprietes de la definition du projet + */ + void fillDefinition(Properties p); + + ///////////////////////////////////////////////////////////////////// + // property change listeners //////////////////////////////////////// + ///////////////////////////////////////////////////////////////////// + void addPropertyChangeListener(PropertyChangeListener listener); + + void addPropertyChangeListener(String propertyName, PropertyChangeListener listener); + + void removePropertyChangeListener(PropertyChangeListener listener); + + void removePropertyChangeListener(String propertyName, PropertyChangeListener listener); + + void removePropertyChangeListeners(); + + void fireAllProperties(); + + +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectConfigurePanelUI.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectConfigurePanelUI.java new file mode 100644 index 0000000..1b27af0 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectConfigurePanelUI.java @@ -0,0 +1,40 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project; + +import org.nuiton.i18n.editor.ui.project.*; +import jaxx.runtime.JAXXObject; + +/** + * Le contrat a respecter pour configurer un type de projet. + * + * @param <P> le type de projet + * + * @author chemit + */ +public interface I18nProjectConfigurePanelUI<P extends I18nProject> extends JAXXObject { + + /** + * + * @return l'instance partagee du modele de creation de projet + */ + ProjectUIModel getModel(); + + P getProject(); +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectFactory.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectFactory.java new file mode 100644 index 0000000..365eb4f --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectFactory.java @@ -0,0 +1,177 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.Set; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; + +/** + * L'usine de projets. + * + * @author chemit + * @see I18nProject + * @see I18nProjectProvider + */ +public class I18nProjectFactory { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(I18nProjectFactory.class); + /** le cache des provideurs de projet. */ + protected static Set<I18nProjectProvider<?>> providers; + + /** + * Creer un projet a partir d'un dictionnaire de parametres. + * + * @param <P> le type de projet + * @param projectClass le type de la classe + * @param params le dictionnaire des paramètres pour créer le projet + * @return le projet instancie mais non ouvert. + */ + public static <P extends I18nProject> P newProject(Class<P> projectClass, Map<String, Object> params) { + I18nProjectProvider<P> provider = getProvider(projectClass); + if (provider == null) { + throw new IllegalArgumentException("could not provider for " + projectClass); + } + P result = provider.newProject(params); + return result; + } + + /** + * Creer un projet a partir de l'ui de creation de projet. + * + * @param <P> le type du projet + * @param projectClass le type du projet + * @param model le model de l'ui de creation de projet + * @return le projet instantie mais non ouvert + */ + public static <P extends I18nProject> P newProject(Class<P> projectClass, ProjectUIModel model) { + + I18nProjectProvider<P> provider = getProvider(projectClass); + if (provider == null) { + throw new IllegalArgumentException("could not provider for " + projectClass); + } + P result = provider.newProject(model); + return result; + } + + /** + * Creer un projet a partir d'un fichier de definition de projet. + * + * @param <P> le type du projet + * @param projectFile le fichier contenant la definition du projet + * @return le projet instantie mais non ouvert. + */ + public static <P extends I18nProject> P newProject(File projectFile) { + + FileInputStream stream = null; + try { + Properties p = new Properties(); + stream = new FileInputStream(projectFile); + p.load(stream); + + String tmp = p.getProperty(AbstractI18nProjectProvider.CLASS_PARAMETER); + Class<? extends I18nProject> projectClass = (Class<? extends I18nProject>) Class.forName(tmp); + + I18nProjectProvider<P> provider = (I18nProjectProvider<P>) getProvider(projectClass); + if (provider == null) { + throw new IllegalArgumentException("could not provider for " + projectClass); + } + // validate definition + provider.validateDefinition(p); + // instanciate safe project + P result = provider.newProject(p); + result.setStoreFile(projectFile); + return result; + + } catch (Exception ex) { + throw new RuntimeException("could not load project for reason " + ex.getMessage(), ex); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException ex) { + throw new RuntimeException("could not close project for reason " + ex.getMessage(), ex); + } + } + } + + + } + + /** + * Recupere l'ensemble des provideurs de projet connus par le systeme. + * + * Note: pour ajouter de nouveaux types de projet, on utilise la mecanique + * de chargement de service de la jdk {@link ServiceLoader}. + * + * @return l'ensemble des provideurs de projet connus par le systeme. + * + */ + public static Set<I18nProjectProvider<?>> getProviders() { + if (providers == null) { + synchronized (I18nProjectProvider.class) { + discoverProviders(); + } + } + return providers; + } + + /** + * Recupere le provideur d'un type de projet donne. + * + * @param <P> le type de projet + * @param projectClass le type de projet + * @return le provideur pour le le type de projet donne, ou <code>null</code> + * si aucun provideur ne prend en charge ce type de projet. + */ + public static <P extends I18nProject> I18nProjectProvider<P> getProvider(Class<P> projectClass) { + + for (I18nProjectProvider<?> p : getProviders()) { + if (p.getType() == projectClass) { + return (I18nProjectProvider<P>) p; + } + } + return null; + } + + /** + * Decouvrer les provideurs de projet via la mecanique {@link ServiceLoader} + * de la jdk + */ + protected static void discoverProviders() { + if (providers == null) { + providers = new HashSet<I18nProjectProvider<?>>(); + } else { + providers.clear(); + } + for (I18nProjectProvider<?> p : ServiceLoader.load(I18nProjectProvider.class)) { + log.info("loaded " + p); + providers.add(p); + } + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectProvider.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectProvider.java new file mode 100644 index 0000000..adf6a21 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/I18nProjectProvider.java @@ -0,0 +1,120 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; + +/** + * Le contrat d'un provider du projet. + * + * @param <P> le type de projet connu par le provider. + * @author chemit + */ +public interface I18nProjectProvider<P extends I18nProject> { + + /** + * + * @return le type du projet + */ + Class<P> getType(); + + /** + * + * @return le label du type de projet + */ + String getLabel(); + + /** + * + * @return la description de ce type de projet + */ + String getDescription(); + + /** + * + * @return le dictionnaire des paramètres autorisés pour ce type de projet + */ + Map<String, Class<?>> getAuthorizedParameters(); + + P newProject(); + /** + * + * @param params les paramètres pour instancier le projet + * @return le projet instancié (mais non chargé?) + */ + P newProject(Map<String, Object> params); + + /** + * + * @param model le model d'ui + * @return le projet instancié (mais non chargé?) + */ + P newProject(ProjectUIModel model); + + /** + * + * @param properties le model d'ui + * @return le projet instancié (mais non chargé?) + */ + P newProject(Properties properties); + + /** + * Decouvre et retourne tous les fichiers de traductions disponibles a + * partir du modele de cration de projet. + * + * @param model le modele a utiliser + * @return la liste des urls des fichiers de traductions trouves a partir + * du modele. + * @throws IOException pour toute erreur + */ + List<URL> detectBundles(ProjectUIModel model) throws IOException; + + /** + * + * @return le type de l'ui pour configurer ce type de projet + */ + Class<? extends I18nProjectConfigurePanelUI<?>> getUIClass(); + + /** + * Valide dans l'ui de creation de projet la partie specifique a + * ce type de projet. + * + * @param model le model a valider + * @return <code>true</code> si la partie specifique a ce type de projet + * est valide, <code>false</code> autrement. + */ + boolean validateUIModel(ProjectUIModel model); + + /** + * Valide un projet a partir de sa definition contenue dans un fichier de + * proprietes. + * + * Cette methode est appellee avant tout chargement de projet en memoire, + * afin de verifier la coherence du projet. + * + * @param properties les proprietes de la definition du projet + * @return <code>true</code> si le projet est valide. + */ + boolean validateDefinition(Properties properties); +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProject.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProject.java new file mode 100644 index 0000000..59e2d94 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProject.java @@ -0,0 +1,67 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project.impl; + +import java.io.File; +import org.nuiton.i18n.editor.project.AbstractI18nProject; +import java.util.Properties; +import org.nuiton.i18n.editor.project.I18nProject; + +/** + * + * @author chemit + */ +public class DirectoryI18nProject extends AbstractI18nProject { + + public static final String DIRECTORY_SOURCE_PROPERTY_NAME = "directorySource"; + /** la source pour un projet de type directory */ + protected File directorySource = new File(""); + + public DirectoryI18nProject() { + super(); + } + + public File getDirectorySource() { + return directorySource; + } + + public void setDirectorySource(File directorySource) { + File oldValue = this.directorySource; + this.directorySource = directorySource; + firePropertyChange(DIRECTORY_SOURCE_PROPERTY_NAME, oldValue, directorySource); + } + + @Override + public void fillDefinition(Properties p) { + super.fillDefinition(p); + p.setProperty(DIRECTORY_SOURCE_PROPERTY_NAME, directorySource.getAbsolutePath()); + } + + @Override + public void copyDefinitionTo(I18nProject project) { + super.copyDefinitionTo(project); + ((DirectoryI18nProject) project).setDirectorySource(directorySource); + } + + @Override + public void fireAllProperties() { + super.fireAllProperties(); + firePropertyChange(DIRECTORY_SOURCE_PROPERTY_NAME, null, directorySource); + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectConfigurePanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectConfigurePanelUI.jaxx new file mode 100644 index 0000000..d21961e --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectConfigurePanelUI.jaxx @@ -0,0 +1,94 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<Table fill='both' constraints='DirectoryI18nProject.class.getName()' + implements='org.nuiton.i18n.editor.project.I18nProjectConfigurePanelUI<DirectoryI18nProject>'> + + <script><![CDATA[ +import jaxx.runtime.SwingUtil; + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.project.impl.*; +import org.nuiton.i18n.editor.ui.project.*; + +import java.io.File; + +protected ProjectUIModel model = getContextValue(ProjectUIModel.class); + +protected DirectoryI18nProject project = model.getProject(DirectoryI18nProject.class); + +@Override +public ProjectUIModel getModel() { + return model; +} + +@Override +public DirectoryI18nProject getProject() { + return project; + //I18nProject p = model.getProject(); + //if (p instanceof DirectoryI18nProject) { + // return (DirectoryI18nProject) p; + //} + //return null; +} + +public void chooseDirectorySource() { + File f = model.chooseDirectory( + this, + _("i18neditor.title.choose.directory.source"), + _("i18neditor.action.choose.directory.source.description"), + getProject().getDirectorySource()); + getProject().setDirectorySource(f); +} +]]> + </script> + + <row> + <cell columns="2"> + <JLabel text='i18neditor.createproject.name.label'/> + </cell> + </row> + <row> + <cell columns="2"> + <JTextField text='{SwingUtil.getStringValue(getProject().getName())}' + onKeyReleased='getProject().setName(((JTextField)event.getSource()).getText())'/> + </cell> + </row> + <row> + <cell columns="2"> + <JLabel text='i18neditor.createproject.directory.source.label'/> + </cell> + </row> + <row> + <cell weightx='1' fill="both"> + <JTextField id="directorySourceFile" + text='{SwingUtil.getStringValue(getProject().getDirectorySource()+"")}' + onKeyReleased='getProject().setDirectorySource(new File(((JTextField)event.getSource()).getText()))'/> + </cell> + <cell anchor="east"> + <JButton actionIcon="fileChooser" + onActionPerformed="chooseDirectorySource()"/> + </cell> + </row> +</Table> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectProvider.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectProvider.java new file mode 100644 index 0000000..d5b1625 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/DirectoryI18nProjectProvider.java @@ -0,0 +1,124 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project.impl; + +import java.io.File; +import java.io.IOException; +import org.nuiton.i18n.editor.project.AbstractI18nProjectProvider; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.editor.project.I18nProjectConfigurePanelUI; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; +import org.nuiton.util.ConverterUtil; + +/** + * + * @author chemit + */ +public class DirectoryI18nProjectProvider extends AbstractI18nProjectProvider<DirectoryI18nProject> { + + public DirectoryI18nProjectProvider() { + super(DirectoryI18nProject.class, I18n.n_("i18neditor.project.type.directory"), I18n.n_("i18neditor.project.type.directory.description")); + } + + @Override + protected Map<String, Class<?>> initAuthorizedParameters() { + Map<String, Class<?>> result = new TreeMap<String, Class<?>>(); + result.put(DirectoryI18nProject.NAME_PROPERTY_NAME, String.class); + result.put(DirectoryI18nProject.URLS_PROPERTY_NAME, List.class); + result.put(DirectoryI18nProject.DIRECTORY_SOURCE_PROPERTY_NAME, File.class); + return result; + } + + @Override + public DirectoryI18nProject newProject() { + return new DirectoryI18nProject(); + } + + @Override + public DirectoryI18nProject newProject(Map<String, Object> params) { + // mandatory parameters + String name = getParameter(String.class, DirectoryI18nProject.NAME_PROPERTY_NAME, params, true); + List<URL> urls = getParameter(List.class, DirectoryI18nProject.URLS_PROPERTY_NAME, params, true); + + // optional parameters + File directorySource = getParameter(File.class, DirectoryI18nProject.DIRECTORY_SOURCE_PROPERTY_NAME, params, false); + + DirectoryI18nProject p = new DirectoryI18nProject(); + p.setName(name); + p.setUrls(urls); + p.setDirectorySource(directorySource); + return p; + } + + @Override + public DirectoryI18nProject newProject(Properties properties) { + Map<String, Object> params = new TreeMap<String, Object>(); + String tmp; + + // name + tmp = properties.getProperty(DirectoryI18nProject.NAME_PROPERTY_NAME); + params.put(DirectoryI18nProject.NAME_PROPERTY_NAME, tmp); + + // urls + tmp = properties.getProperty(DirectoryI18nProject.URLS_PROPERTY_NAME); + String[] urlsStr = tmp.split(","); + List<URL> urls = new ArrayList<URL>(); + for (String u : urlsStr) { + URL url = ConverterUtil.convert(URL.class, u); + urls.add(url); + } + params.put(DirectoryI18nProject.URLS_PROPERTY_NAME, urls); + + // directorySource + tmp = properties.getProperty(DirectoryI18nProject.DIRECTORY_SOURCE_PROPERTY_NAME); + File directorySource = new File(tmp); + params.put(DirectoryI18nProject.DIRECTORY_SOURCE_PROPERTY_NAME, directorySource); + DirectoryI18nProject p = newProject(params); + return p; + } + + @Override + public List<URL> detectBundles(ProjectUIModel model) throws IOException { + //TODO + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean validateUIModel(ProjectUIModel model) { + DirectoryI18nProject project = (DirectoryI18nProject) model.getProject(); + return project.getDirectorySource() != null && project.getDirectorySource().exists() && project.getName() != null && !project.getName().isEmpty(); + } + + @Override + public boolean validateDefinition(Properties properties) { + //TODO + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Class<? extends I18nProjectConfigurePanelUI<?>> getUIClass() { + return DirectoryI18nProjectConfigurePanelUI.class; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProject.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProject.java new file mode 100644 index 0000000..9e034c2 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProject.java @@ -0,0 +1,86 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project.impl; + +import java.io.File; +import java.util.Properties; +import org.nuiton.i18n.editor.project.AbstractI18nProject; +import org.nuiton.i18n.editor.project.I18nProject; + +/** + * + * @author chemit + */ +public class JarI18nProject extends AbstractI18nProject { + + public static final String JAR_SOURCE_PROPERTY_NAME = "jarSource"; + public static final String UNIQUE_JAR_DEFINITION_PROPERTY_NAME = "uniqueJarDefinition"; + /** la source pour un projet de type jar */ + protected File jarSource = new File(""); + /** + * un drapeau (pour les projets de type jar) pour savoir si on utilise le + * système i18n avec nom unique. + */ + protected boolean uniqueJarDefinition; + + public JarI18nProject() { + super(); + } + + public File getJarSource() { + return jarSource; + } + + public void setJarSource(File jarSource) { + File oldValue = this.jarSource; + this.jarSource = jarSource; + firePropertyChange(JAR_SOURCE_PROPERTY_NAME, oldValue, jarSource); + } + + public boolean isUniqueJarDefinition() { + return uniqueJarDefinition; + } + + public void setUniqueJarDefinition(boolean uniqueJarDefinition) { + boolean oldValue = this.uniqueJarDefinition; + this.uniqueJarDefinition = uniqueJarDefinition; + firePropertyChange(UNIQUE_JAR_DEFINITION_PROPERTY_NAME, oldValue, uniqueJarDefinition); + } + + @Override + public void fillDefinition(Properties p) { + super.fillDefinition(p); + p.setProperty(JAR_SOURCE_PROPERTY_NAME, jarSource.getAbsolutePath()); + p.setProperty(UNIQUE_JAR_DEFINITION_PROPERTY_NAME, Boolean.valueOf(uniqueJarDefinition).toString()); + } + + @Override + public void copyDefinitionTo(I18nProject project) { + super.copyDefinitionTo(project); + ((JarI18nProject) project).setJarSource(jarSource); + ((JarI18nProject) project).setUniqueJarDefinition(uniqueJarDefinition); + } + + @Override + public void fireAllProperties() { + super.fireAllProperties(); + firePropertyChange(JAR_SOURCE_PROPERTY_NAME, null, jarSource); + firePropertyChange(UNIQUE_JAR_DEFINITION_PROPERTY_NAME, null, uniqueJarDefinition); + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectConfigurePanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectConfigurePanelUI.jaxx new file mode 100644 index 0000000..17aee4b --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectConfigurePanelUI.jaxx @@ -0,0 +1,106 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<Table fill='both' constraints='JarI18nProject.class.getName()' + implements='org.nuiton.i18n.editor.project.I18nProjectConfigurePanelUI<JarI18nProject>'> + + <script><![CDATA[ +import jaxx.runtime.SwingUtil; + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.project.impl.*; +import org.nuiton.i18n.editor.ui.project.*; + +import java.io.File; + +protected ProjectUIModel model = getContextValue(ProjectUIModel.class); + +protected JarI18nProject project = model.getProject(JarI18nProject.class); + +@Override +public ProjectUIModel getModel() { + return model; +} + +@Override +public JarI18nProject getProject() { + return project; + //I18nProject p = model.getProject(); + //if (p instanceof JarI18nProject) { + // return (JarI18nProject) p; + //} + //return null; +} + +public void chooseJarSource() { + File f = model.chooseFile ( + this, + _("i18neditor.title.choose.jar.source"), + _("i18neditor.action.choose.jar.source"), + getProject().getJarSource(), + "^.+\\.jar$", + _("i18neditor.action.choose.jar.source.description")); + getProject().setJarSource(f); +} +]]> + </script> + + <row> + <cell columns="2"> + <JLabel text='i18neditor.createproject.name.label'/> + </cell> + </row> + <row> + <cell columns="2"> + <JTextField text='{SwingUtil.getStringValue(getProject().getName())}' + onKeyReleased='getProject().setName(((JTextField)event.getSource()).getText())'/> + </cell> + </row> + <row> + <cell columns="2"> + <JLabel text='i18neditor.createproject.jar.source.label'/> + </cell> + </row> + <row> + <cell weightx='1' fill="both"> + <JTextField id="jarSourceFile" + text='{SwingUtil.getStringValue(getProject().getJarSource()+"")}' + onKeyReleased='getProject().setJarSource(new File(((JTextField)event.getSource()).getText()))'/> + </cell> + <cell anchor="east"> + <JButton actionIcon="fileChooser" + onActionPerformed="chooseJarSource()"/> + </cell> + </row> + <row> + <cell columns="2"> + <JCheckBox id='uniqueJarDefinition' + text='i18neditor.createproject.uniqueJarDefinition' + toolTipText='i18neditor.createproject.uniqueJarDefinition.tip' + selected='{getProject().isUniqueJarDefinition()}' + onItemStateChanged='getProject().setUniqueJarDefinition(event.getStateChange() == ItemEvent.SELECTED)'/> + </cell> + </row> +</Table> + diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectProvider.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectProvider.java new file mode 100644 index 0000000..828da7a --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/project/impl/JarI18nProjectProvider.java @@ -0,0 +1,186 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.project.impl; + +import java.io.File; +import java.io.IOException; +import org.nuiton.i18n.editor.project.AbstractI18nProjectProvider; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.bundle.I18nBundleFactory; +import org.nuiton.i18n.editor.project.I18nProjectConfigurePanelUI; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; +import org.nuiton.util.ConverterUtil; +import org.nuiton.util.Resource; + +/** + * + * @author chemit + */ +public class JarI18nProjectProvider extends AbstractI18nProjectProvider<JarI18nProject> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(JarI18nProjectProvider.class); + + public JarI18nProjectProvider() { + super(JarI18nProject.class, + I18n.n_("i18neditor.project.type.jar"), + I18n.n_("i18neditor.project.type.jar.description")); + } + + @Override + protected Map<String, Class<?>> initAuthorizedParameters() { + Map<String, Class<?>> result = new TreeMap<String, Class<?>>(); + result.put(JarI18nProject.NAME_PROPERTY_NAME, String.class); + result.put(JarI18nProject.URLS_PROPERTY_NAME, List.class); + result.put(JarI18nProject.JAR_SOURCE_PROPERTY_NAME, File.class); + result.put(JarI18nProject.UNIQUE_JAR_DEFINITION_PROPERTY_NAME, Boolean.class); + return result; + } + + @Override + public JarI18nProject newProject() { + return new JarI18nProject(); + } + + @Override + public JarI18nProject newProject(Map<String, Object> params) { + + // mandatory parameters + String name = getParameter(String.class, JarI18nProject.NAME_PROPERTY_NAME, params, true); + List<URL> urls = getParameter(List.class, JarI18nProject.URLS_PROPERTY_NAME, params, true); + + // optional parameters + File jarSource = getParameter(File.class, JarI18nProject.JAR_SOURCE_PROPERTY_NAME, params, false); + Boolean uniqueJarDefinition = getParameter(Boolean.class, JarI18nProject.UNIQUE_JAR_DEFINITION_PROPERTY_NAME, params, false); + JarI18nProject p = new JarI18nProject(); + p.setName(name); + p.setUrls(urls); + p.setJarSource(jarSource); + p.setUniqueJarDefinition(uniqueJarDefinition != null && uniqueJarDefinition); + return p; + } + + @Override + public JarI18nProject newProject(Properties properties) { + Map<String, Object> params = new TreeMap<String, Object>(); + String tmp; + + // name + tmp = properties.getProperty(JarI18nProject.NAME_PROPERTY_NAME); + params.put(JarI18nProject.NAME_PROPERTY_NAME, tmp); + + // urls + tmp = properties.getProperty(JarI18nProject.URLS_PROPERTY_NAME); + String[] urlsStr = tmp.split(","); + List<URL> urls = new ArrayList<URL>(); + for (String u : urlsStr) { + URL url = ConverterUtil.convert(URL.class, u); + urls.add(url); + } + params.put(JarI18nProject.URLS_PROPERTY_NAME, urls); + + // jarSource + tmp = properties.getProperty(JarI18nProject.JAR_SOURCE_PROPERTY_NAME); + File jarSource = new File(tmp); + params.put(JarI18nProject.JAR_SOURCE_PROPERTY_NAME, jarSource); + + // uniqueJarDefinition + tmp = properties.getProperty(JarI18nProject.UNIQUE_JAR_DEFINITION_PROPERTY_NAME, "false"); + Boolean uniqueJarDefinition = Boolean.valueOf(tmp); + params.put(JarI18nProject.UNIQUE_JAR_DEFINITION_PROPERTY_NAME, uniqueJarDefinition); + + JarI18nProject p = newProject(params); + return p; + } + + @Override + public List<URL> detectBundles(ProjectUIModel model) throws IOException { + List<URL> urls = new ArrayList<URL>(); + JarI18nProject project = (JarI18nProject) model.getProject(); + URL source = project.getJarSource().toURI().toURL(); + URLClassLoader loader = new URLClassLoader(new URL[]{source}); + log.info("jar source : " + source); + if (project.isUniqueJarDefinition()) { + // on recherche l'unique bundle dans META-INF/ + List<URL> defs = Resource.getURLs("META-INF/.*-i18n-definition\\.properties", loader); + log.info("detected unique bundle definition " + defs); + if (!defs.isEmpty()) { + // on ne traite que le premier ??? + URL u = defs.get(0); + String path = u.toString(); + int index = path.lastIndexOf("/"); + path = path.substring(index + 1); + String bundleName = path.substring(0, path.length() - "-i18n-definition.properties".length()); + log.info("bundleName : " + bundleName); + urls = Resource.getURLs("META-INF/" + bundleName + "-.*\\.properties", loader); + urls.remove(u); + } + } else { + urls = Resource.getURLs(I18nBundleFactory.SEARCH_BUNDLE_PATTERN, loader); + log.info("detected bundles : " + urls); + } + return urls; + } + + @Override + public boolean validateUIModel(ProjectUIModel model) { + JarI18nProject project = (JarI18nProject) model.getProject(); + return project.getJarSource() != null && project.getJarSource().exists() && project.getName() != null && !project.getName().isEmpty(); + } + + @Override + public boolean validateDefinition(Properties properties) { + + // verifie que les proprietes sont presentes : + if (!properties.containsKey(CLASS_PARAMETER)) { + throw new IllegalStateException("could not find property " + CLASS_PARAMETER); + } + if (!properties.containsKey(JarI18nProject.NAME_PROPERTY_NAME)) { + throw new IllegalStateException("could not find property " + JarI18nProject.NAME_PROPERTY_NAME); + } + if (!properties.containsKey(JarI18nProject.URLS_PROPERTY_NAME)) { + throw new IllegalStateException("could not find property " + JarI18nProject.URLS_PROPERTY_NAME); + } + // verifie que les urls existent + String tmp = properties.getProperty(JarI18nProject.URLS_PROPERTY_NAME); + String[] urlsStr = tmp.split(","); + for (String u : urlsStr) { + int lastIndex = u.lastIndexOf("/"); + u = u.substring(0, lastIndex); + URL url = ConverterUtil.convert(URL.class, u); + // check if url is an existing file + log.info(url + " to check"); + } + return false; + } + + @Override + public Class<? extends I18nProjectConfigurePanelUI<?>> getUIClass() { + return JarI18nProjectConfigurePanelUI.class; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleCheckBoxMenuUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleCheckBoxMenuUI.jaxx new file mode 100644 index 0000000..ecebe70 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleCheckBoxMenuUI.jaxx @@ -0,0 +1,56 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JCheckBoxMenuItem id='content' text='{getBundle().getDisplayName()}' toolTipText='{_("i18neditor.bundle.name", getBundle().getDisplayName())}' onItemStateChanged='updateState(event)'> + + <!-- le nom du bundle --> + <Locale id='bundle' javaBean='Locale.FRANCE'/> + + <script><![CDATA[ +import org.nuiton.i18n.editor.*; +import java.util.Locale; +import jaxx.runtime.JAXXContext; + +public BundleCheckBoxMenuUI(JAXXContext parentContext, Locale bundle, boolean selected) { + super(null, selected); + setContextValue(parentContext); + setBundle(bundle); +} + +protected void updateState(ItemEvent e) { + I18nEditorContext context = getContextValue(I18nEditorContext.class); + if (context == null) { + // pas encore de context (ui en construction) + return; + } + boolean selected = e.getStateChange() == ItemEvent.SELECTED; + if (selected) { + context.addSelectedBundle(bundle); + } else { + context.removeSelectedBundle(bundle); + } +} +]]> + </script> + +</JCheckBoxMenuItem> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.css new file mode 100644 index 0000000..d95ab1e --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.css @@ -0,0 +1,52 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +JToolBar { + borderPainted:false; + floatable:false; + opaque:false; +} + +#reset { + actionIcon:"cancel"; + toolTipText:"i18neditor.action.reset.value.tip"; + enabled:{isModified()}; +} +#delete{ + actionIcon:"delete"; + toolTipText:"i18neditor.action.delete.key.tip"; +} +#save { + actionIcon:"save"; + toolTipText:"i18neditor.action.save.value.tip"; + enabled:{isModified()}; +} + +#label { + text:{updateLabel(getBundle())}; +} + +#editorPane { + columnHeaderView:{toolbar}; +} + +#editor { + wrapStyleWord:true; + lineWrap:true; + text:{SwingUtil.getStringValue(getInitialValue())}; +} \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.jaxx new file mode 100644 index 0000000..2fdf5ce --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValueUI.jaxx @@ -0,0 +1,87 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JPanel layout='{new BorderLayout()}'> + + <style source='BundleValueUI.css'/> + + <!-- la locale --> + <Locale id='bundle' javaBean='null'/> + + <!-- le nom du package --> + <String id='packageName' javaBean='null'/> + + <!-- la clef --> + <String id='key' javaBean='null'/> + + <!-- la valeur initiale --> + <String id='initialValue' javaBean='null'/> + + <Boolean id='modified' javaBean='Boolean.FALSE'/> + + <script><![CDATA[ +import java.util.Locale; +import jaxx.runtime.SwingUtil; +import org.nuiton.i18n.editor.*; + +protected I18nEditorUIHandler handler = getContextValue(I18nEditorUIHandler.class); +protected I18nEditorContext context = getContextValue(I18nEditorContext.class); + +public void updateValue(String newValue) { + if (initialValue.equals(newValue)) { + // valeur non changee + return; + } + setModified(true); +} + +protected String updateLabel(Locale l) { + String result = null; + if (l != null) { + result = _("i18neditor.bundle.name", l.getDisplayName()); + } + return result; +} + +protected String getValue() { + return editor.getText(); +} +]]> + </script> + + <JToolBar id='toolbar'> + + <JLabel id='label'/> + + <javax.swing.Box.Filler constructorParams='SwingUtil.newMinDimension(), SwingUtil.newMinDimension(), SwingUtil.newMaxXDimension()'/> + + <JButton id='delete' onActionPerformed='handler.deleteSelectedValueForLocale(context, this, packageName, bundle, key)'/> + <JButton id='reset' onActionPerformed='String t = initialValue; initialValue=null; setInitialValue(t);setModified(false);'/> + <JButton id='save' onActionPerformed='handler.updateSelectedValue(context, packageName, bundle, key, getValue()); initialValue = getValue();setModified(false);'/> + </JToolBar> + + <JScrollPane id='editorPane' constraints='BorderLayout.CENTER'> + <JTextArea id='editor' + onKeyReleased='updateValue(((JTextArea) event.getSource()).getText())'/> + </JScrollPane> +</JPanel> \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValuesUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValuesUI.jaxx new file mode 100644 index 0000000..a2a17ae --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/BundleValuesUI.jaxx @@ -0,0 +1,54 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<org.jdesktop.swingx.JXTitledPanel id='content' + title='{updateTitle(getPackageName())}'> + + <!-- les locales --> + <java.util.List id='bundles' javaBean='null' genericType='Locale'/> + + <!-- le nom du package --> + <String id='packageName' javaBean='null'/> + + <!-- le nom de la cef --> + <String id='key' javaBean='null'/> + + <script><![CDATA[ +protected String updateTitle(String l) { + String result = ""; + if (l != null) { + int index = l.lastIndexOf("/"); + if (index > -1) { + l = l.substring(index + 1); + } + result = _("i18neditor.package.name", l); + } + return result; +} +]]> + </script> + <JPanel id='bundlesContent' + layout='{new GridLayout(0,1)}' + border='{BorderFactory.createTitledBorder((String)null)}' + preferredSize='{new Dimension(-1, 150)}'/> +</org.jdesktop.swingx.JXTitledPanel> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.css new file mode 100644 index 0000000..6894a10 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.css @@ -0,0 +1,43 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +#countryLabel { + text:"i18neditor.createbundle.newlocale"; + labelFor:{country}; +} + +#languageLabel { + text:"i18neditor.createbundle.newlanguage"; + labelFor:{language}; +} + +#label { + text:"i18neditor.createbundle.label"; +} +#buttonCancel { + text:"i18neditor.action.cancel"; +} + +#buttonCreate{ + text:"i18neditor.action.create"; +} + +#main { + title:"i18neditor.createbundle.title"; + modal:true; +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.jaxx new file mode 100644 index 0000000..eb0833f --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreateBundleUI.jaxx @@ -0,0 +1,80 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JDialog id='main'> + + <style source='CreateBundleUI.css'/> + + <String id='bundle'/> + <script><![CDATA[ +import java.util.Locale; +import org.nuiton.i18n.editor.*; + +public boolean acceptLocale(Locale l, String expected) { + return l !=null && l.toString().equals(expected); +} + +public I18nEditorUIHandler getHandler() { + return getContextValue(I18nEditorUIHandler.class); +} + +protected I18nEditorContext getContext() { + return I18nEditorContext.get(); +} + +void $afterCompleteSetup() { + + //getBroker().setHandler(getHandler()); +} +]]> + </script> + <JPanel layout='{new BorderLayout()}'> + <JPanel constraints='BorderLayout.NORTH' background='{Color.WHITE}'> + <JLabel id='label'/> + </JPanel> + + <Table fill="both" constraints='BorderLayout.CENTER'> + <row weightx="1" weighty="1"> + <cell> + <JLabel id='languageLabel'/> + </cell> + <cell> + <jaxx.runtime.swing.editor.EnumEditor id='language' constructorParams='org.nuiton.i18n.LanguageEnum.class'/> + </cell> + </row> + <row weightx="1" weighty="1"> + <cell> + <JLabel id='countryLabel'/> + </cell> + <cell> + <jaxx.runtime.swing.editor.EnumEditor id='country' constructorParams='org.nuiton.i18n.CountryEnum.class'/> + </cell> + </row> + </Table> + + <JPanel constraints='BorderLayout.SOUTH' layout='{new GridLayout()}'> + <JButton id='buttonCancel' onActionPerformed='bundle = null;dispose();'/> + <JButton id='buttonCreate' onActionPerformed='getContext().createBundle(bundle = language.getSelectedItem() + "_" + country.getSelectedItem());dispose();'/> + </JPanel> + </JPanel> +</JDialog> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.css new file mode 100644 index 0000000..2722155 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.css @@ -0,0 +1,40 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +#packageLabel { + text:"i18neditor.createpackage.newpackage"; + labelFor:{packageName}; +} + +#label { + text:"i18neditor.createpackage.label"; +} + +#buttonCancel { + text:"i18neditor.action.cancel"; +} + +#buttonCreate{ + text:"i18neditor.action.create"; +} + +#main { + title:"i18neditor.createpackage.title"; + modal:true; +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.jaxx new file mode 100644 index 0000000..b2ac183 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/CreatePackageUI.jaxx @@ -0,0 +1,72 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JDialog id='main'> + + <style source='CreatePackageUI.css'/> + + <String id='bundle'/> + <script><![CDATA[ +import java.util.Locale; +import org.nuiton.i18n.editor.*; + +public boolean acceptLocale(Locale l, String expected) { + return l !=null && l.toString().equals(expected); +} + +public I18nEditorUIHandler getHandler() { + return getContextValue(I18nEditorUIHandler.class); +} + +protected I18nEditorContext getContext() { + return I18nEditorContext.get(); +} + +void $afterCompleteSetup() { + + //getBroker().setHandler(getHandler()); +} +]]> + </script> + <JPanel layout='{new BorderLayout()}'> + <JPanel constraints='BorderLayout.NORTH' background='{Color.WHITE}'> + <JLabel id='label'/> + </JPanel> + + <Table fill="both" constraints='BorderLayout.CENTER'> + <row weightx="1" weighty="1"> + <cell> + <JLabel id='packageLabel'/> + </cell> + <cell> + <JTextField id='packageName'/> + </cell> + </row> + </Table> + + <JPanel constraints='BorderLayout.SOUTH' layout='{new GridLayout()}'> + <JButton id='buttonCancel' onActionPerformed='bundle = null;dispose();'/> + <JButton id='buttonCreate' onActionPerformed='getContext().createPackage(bundle = packageName.getText());dispose();'/> + </JPanel> + </JPanel> +</JDialog> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.css new file mode 100644 index 0000000..3b92b28 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.css @@ -0,0 +1,260 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* +*/ + +JToolBar { + borderPainted:false; + floatable:false; + opaque:false; +} + +JSplitPane { + oneTouchExpandable:true; + continuousLayout:true; + dividerSize:6; +} + +#mainFrame { + defaultCloseOperation:"do_nothing_on_close"; + title:{updateTitle(context.getProject())}; +/*iconImage:{createImageIcon("logo-OT_web.png").getImage()};*/ +} + +#menu { + _help:{"ui.main.menu"}; +} + +#menuFile { + text:"i18neditor.main.menu.file"; + mnemonic:F; + _help:{"ui.main.menu.file"}; +} + +#menuFileConfiguration { + text:"i18neditor.action.configuration"; + toolTipText:"i18neditor.action.configuration.tip"; + actionIcon:"config"; + mnemonic:C; + _help:{"ui.main.menu.file.configuration"}; +} + +#menuFileEditProject { + text:"i18neditor.action.editProject"; + actionIcon:"edit"; + enabled:{context.getProject() != null}; +} + +#menuFileCreateProject { + text:"i18neditor.action.createProject"; + actionIcon:"add"; + enabled:{context.getProject() == null}; +} + +#menuFileOpenProject { + text:"i18neditor.action.openProject"; + actionIcon:"open"; + enabled:{context.getProject() == null}; +} + +#menuFileOpenProjectOther{ + text:"i18neditor.action.openOtherProject"; + actionIcon:"open"; +} + +#menuFileDeleteProject{ + text:"i18neditor.action.deleteProject"; + actionIcon:"delete"; +} + +#menuFileCloseProject { + text:"i18neditor.action.closeProject"; + actionIcon:"close"; + enabled:{context.getProject() != null}; +} +#menuFileStore { + text:"i18neditor.menu.storeall"; + actionIcon:"save"; + enabled:{context.getProject() != null}; +} + +#menuFileFullscreen { + text:"i18neditor.action.fullscreen"; + toolTipText:"i18neditor.action.fullscreen.tip"; + actionIcon:"fullscreen"; + mnemonic:P; + visible:{!isUndecorated()}; +_help:{"ui.main.menu.file.fullscreen"}; +} + +#menuFileNormalscreen { + text:"i18neditor.action.normalscreen"; + toolTipText:"i18neditor.action.normalscreen.tip"; + actionIcon:"leave-fullscreen"; + mnemonic:N; + visible:{isUndecorated()}; +_help:{"ui.main.menu.file.leave-fullscreen"}; +} + +#menuFileQuit{ + text:"i18neditor.action.exit"; + toolTipText:"i18neditor.action.exit.tip"; + actionIcon:"exit"; + mnemonic:X; + _help:{"ui.main.menu.file.exit"}; +} + +#menuPackages { + text:"i18neditor.menu.packages" ; + mnemonic:P; + enabled:{context.getProject() != null}; +} + +#menuPackagesCreate { + text:"i18neditor.action.new.package" ; + actionIcon:"add"; + mnemonic:N; +} + +#menuBundles { + text:"i18neditor.menu.bundles"; + mnemonic:B; + enabled:{context.getProject() != null}; +} + +#menuBundlesCreate { + text:"i18neditor.action.new.bundle" ; + actionIcon:"add"; + mnemonic:N; +} + + +#menuHelp{ + text:"i18neditor.menu.help"; + mnemonic:e; + _help:{"ui.main.menu.help"}; +} + +#menuHelpHelp{ + text:"i18neditor.action.help"; + toolTipText:"i18neditor.action.help.tip"; + actionIcon:"help"; + mnemonic:e; + _help:{"ui.main.menu.help.help"}; +} + +#menuHelpSite{ + text:"i18neditor.action.site"; + toolTipText:"i18neditor.action.site.tip"; + actionIcon:"site"; + mnemonic:s; + _help:{"ui.main.menu.help.site"}; +} + +#menuHelpAbout{ + text:"i18neditor.action.about"; + toolTipText:"i18neditor.action.about.tip"; + actionIcon:"about"; + mnemonic:A; + _help:{"ui.main.menu.help.about"}; +} + +#showHelp{ + toolTipText:"i18neditor.action.showHelp.tip"; + actionIcon:"show-help"; + borderPainted:false; + visible:true; +} + +#showProjectInfo { + actionIcon:"information"; + toolTipText:"i18neditor.project.info.tip"; + borderPainted:false; + rolloverEnabled:false; +} + +#closeTab{ + actionIcon:"closeTab"; + borderPainted:false; + rolloverEnabled:false; + focusable:false; + toolTipText:"i18neditor.action.quitHelp.tip"; + _help:{"ui.main.body.help.closeTab"}; +} + +#keysView{ + _help:{"ui.main.body.view.keys"}; +} + +#valuesView{ + _help:{"ui.main.body.view.values"}; +} + +#splitpane{ + orientation:{JSplitPane.HORIZONTAL_SPLIT}; +_help:{"ui.main.body.db"}; +/*resizeWeight:1.0;*/ +} + +#toolbar{ + _help:{"ui.main.toolbar"}; +} + +#treeToolBar{ + enabled:{context.getProject() != null}; +} + +#toTreeMode { + + buttonGroup:"treeModelMode"; + text:{TreeModelMode.TREE.toString()}; +enabled:{context.getProject() != null}; +selected:{context.getTreeModelMode() == TreeModelMode.TREE}; +value:{TreeModelMode.TREE}; +} + +#toFlatMode { + buttonGroup:"treeModelMode"; + text:{TreeModelMode.FLAT.toString()}; +enabled:{context.getProject() != null}; +selected:{context.getTreeModelMode() == TreeModelMode.FLAT}; +value:{TreeModelMode.FLAT}; +} + +#collapseAll { + actionIcon:"collapseAll"; + enabled:{context.getProject() != null && context.getSelectedKey() != null}; +} + +#expandAll{ + actionIcon:"expandAll"; + enabled:{context.getProject() != null && context.getSelectedKey() != null}; +} + +#keysView { + columnHeaderView:{treeToolBar}; +} + +#tree{ + showsRootHandles:false; + rootVisible:false; + selectionModel:{treeSelectionModel}; +} + +#valuesView { + horizontalScrollBarPolicy:horizontal_scrollbar_never; +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.jaxx new file mode 100644 index 0000000..012e587 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUI.jaxx @@ -0,0 +1,343 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JFrame id='mainFrame' implements='PropertyChangeListener' + width='800' + height='800' + onWindowClosing='handler.close(context)' + undecorated='{config.isFullScreen()}'> + + <style source='I18nEditorUI.css'/> + + <script><![CDATA[ +import jaxx.runtime.SwingUtil; +import javax.swing.tree.*; +import java.util.Locale; +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.I18nProject; + +protected I18nEditorUIHandler handler = getContextValue(I18nEditorUIHandler.class); +protected I18nEditorConfig config = getContextValue(I18nEditorConfig.class); +protected I18nEditorContext context = getContextValue(I18nEditorContext.class); + +@Override +public void propertyChange(PropertyChangeEvent evt) { + String name = evt.getPropertyName(); + if (log.isDebugEnabled()) { + log.debug(name+" <old:"+evt.getOldValue()+" - new:"+evt.getNewValue()+">"); + } +// log.info(name+" <old:"+evt.getOldValue()+" - new:"+evt.getNewValue()+">"); + if (I18nEditorContext.SELECTED_KEY_PROPERTY.equals(name)) { + // mise a jour du menu packages + String newValue = (String) evt.getNewValue(); + handler.reloadValues(this, newValue); + selectKeyInTree(newValue); + return; + } + if (I18nEditorContext.PROJECT_PROPERTY.equals(name)) { + I18nProject newValue = (I18nProject) evt.getNewValue(); + I18nProject oldValue = (I18nProject) evt.getOldValue(); + if (newValue != null) { + handler.openProjectUI( this, newValue); + } else { + handler.closeProjectUI( this, oldValue); + } + return; + } + if (I18nEditorContext.SELECTED_BUNDLES_PROPERTY.equals(name)) { + context.saveSelectedKey(); + handler.reloadKeys(this); + handler.reloadValues(this); + return; + } + if (I18nEditorContext.SELECTED_PACKAGES_PROPERTY.equals(name)) { + context.saveSelectedKey(); + handler.reloadKeys(this); + handler.reloadValues(this); + return; + } + if (I18nEditorContext.TREE_MODEL_MODE_PROPERTY.equals(name)) { + handler.reloadKeys(this); + handler.reloadValues(this); + return; + } + if (jaxx.runtime.swing.editor.I18nEditor.SELECTED_LOCALE_PROPERTY.equals(name)) { + // mise a jour du menu packages + Locale newValue = (Locale) evt.getNewValue(); + handler.changeLanguage(context, newValue); + return; + } + if ("projects".equals(name)) { + // mise a jour du menu projects + handler.updateProjectsMenuUI(context); + return; + } +} + +public String updateTitle(I18nProject p) { + String txt = null; + if (p==null) { + txt = _("i18neditor.title.noproject"); + } else { + txt = _("i18neditor.title", p.getName() + " - " + p.getStoreFile()); + } + return txt; +} +public void updateSelectedKey(PropertieNode node) { + String key = null; + if (node == null && !treeSelectionModel.isSelectionEmpty()) { + node = (PropertieNode) tree.getSelectionPath().getLastPathComponent(); + } + if (node != null) { + key = node.getUserObject().toString(); + } + log.debug(node + " : " + key); + context.setSelectedKey(key); +} + +public void selectKeyInTree(final String selectedKey) { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + log.debug("selectKeyInTree ==========================================================="); + if (selectedKey == null) { + // pas de clef selectionnee + log.debug("no key to select (selectedKey null or not in model)!"); + if (!treeSelectionModel.isSelectionEmpty()) { + // on vide la selection dans l'arbre + treeSelectionModel.clearSelection(); + } + return; + } + log.debug("reselected key " + selectedKey); + PropertieNode selectedNode = treeModel.getNode(selectedKey); + log.debug("reselected node " + selectedNode); + TreePath path = new TreePath(treeModel.getPathToRoot(selectedNode)); + log.debug("reselected path " + path); + tree.setSelectionPath(path); + tree.scrollPathToVisible(path); + TreePath leadSelectionPath = treeSelectionModel.getLeadSelectionPath(); + log.debug("new selected path " + leadSelectionPath); + log.debug("selectKeyInTree ==========================================================="); + } + }); +} + +public void collapseAll(final PropertieNode node) { + if (node != null) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + TreePath path = new TreePath(treeModel.getPathToRoot(node)); + if (path != null) { + tree.collapsePath(path); + //TODO faire un collapse recursive + } + } + }); + } +} + +public void expandAll(final PropertieNode node) { + if (node != null) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + TreePath path = new TreePath(treeModel.getPathToRoot(node)); + if (path != null) { + tree.expandPath(path); + //TODO faire un expand recursive + } + } + }); + } +} + +public I18nEditorContext getContext() { + return context; +} + +@Override +public void dispose() { + super.dispose(); + context.removePropertyChangeListener(I18nEditorContext.SELECTED_PACKAGES_PROPERTY, this); + context.removePropertyChangeListener(I18nEditorContext.SELECTED_BUNDLES_PROPERTY, this); + context.removePropertyChangeListener(I18nEditorContext.SELECTED_KEY_PROPERTY, this); + context.removePropertyChangeListener(I18nEditorContext.PROJECT_PROPERTY, this); + context.removePropertyChangeListener(I18nEditorContext.TREE_MODEL_MODE_PROPERTY, this); + context.removePropertyChangeListener("projects", this); + context.removePropertyChangeListener(this); +} + +// register listeners +changeI18n.addPropertyChangeListener(jaxx.runtime.swing.editor.I18nEditor.SELECTED_LOCALE_PROPERTY, this); +context.addPropertyChangeListener(I18nEditorContext.SELECTED_PACKAGES_PROPERTY, this); +context.addPropertyChangeListener(I18nEditorContext.SELECTED_BUNDLES_PROPERTY, this); +context.addPropertyChangeListener(I18nEditorContext.SELECTED_KEY_PROPERTY, this); +context.addPropertyChangeListener(I18nEditorContext.PROJECT_PROPERTY, this); +context.addPropertyChangeListener(I18nEditorContext.TREE_MODEL_MODE_PROPERTY, this); +context.addPropertyChangeListener("projects", this); + +treeSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { + + @Override + public void valueChanged(TreeSelectionEvent e) { + TreePath path = e.getPath(); + TreeNode node = (TreeNode) path.getLastPathComponent(); + log.debug(path + " " + node.isLeaf()); + if (!node.isLeaf() && !tree.isExpanded(path)) { + // expand the node to avoid a click :) + log.info("expand path " + path); + tree.expandPath(path); + } + } +}); + +// register help broker +//broker.setHandler(handler); + +]]> + </script> + <JMenuBar> + <JMenu id='menuFile'> + <JMenuItem id='menuFileConfiguration' onActionPerformed="handler.showConfig(context)"/> + <JMenuItem id="menuFileCreateProject" onActionPerformed="handler.createProject(context)"/> + <JMenuItem id="menuFileEditProject" onActionPerformed="handler.editProject(context)"/> + <JMenu id="menuFileOpenProject"> + <JMenuItem id="menuFileOpenProjectOther" + onActionPerformed="handler.openOtherProject(context)" + _doNotRemove='Boolean.TRUE'/> + <JMenuItem id="menuFileDeleteProject" + onActionPerformed="handler.deleteProject(context)" + _doNotRemove='Boolean.TRUE'/> + <JSeparator/> + </JMenu> + + <JMenuItem id="menuFileStore" onActionPerformed="context.store()"/> + <JMenuItem id="menuFileCloseProject" onActionPerformed="handler.closeProject(context, context.getProject())"/> + <JSeparator/> + <JMenuItem id='menuFileFullscreen' onActionPerformed="handler.changeScreen(context, true)"/> + <JMenuItem id='menuFileNormalscreen' onActionPerformed="handler.changeScreen(context, false)"/> + <JSeparator/> + <JMenuItem id="menuFileQuit" onActionPerformed="handler.close(context)"/> + </JMenu> + <JMenu id='menuPackages'> + <JMenuItem id="menuPackagesCreate" onActionPerformed="handler.createPackage(context)"/> + <JSeparator/> + </JMenu> + <JMenu id='menuBundles'> + <JMenuItem id="menuBundlesCreate" onActionPerformed="handler.createBundle(context)"/> + <JSeparator/> + </JMenu> + <JMenu id='menuHelp'> + <JMenuItem id='menuHelpHelp' onActionPerformed="handler.showHelp(context, null)"/> + <JMenuItem id='menuHelpSite' onActionPerformed="handler.gotoSite(context)"/> + <JMenuItem id='menuHelpAbout' onActionPerformed="handler.showAbout(context)"/> + </JMenu> + <JToolBar id='toolbar' _help='"ui.main.toolbar"'> + + <JButton id='showHelp'/> + + <javax.swing.Box.Filler constructorParams='SwingUtil.newMinDimension(), SwingUtil.newMinDimension(), SwingUtil.newMaxXDimension()'/> + <JSeparator orientation='vertical'/> + <JButton id='showProjectInfo' onActionPerformed='handler.showStorageInfo(context)'/> + + <jaxx.runtime.swing.editor.I18nEditor id='changeI18n' + opaque='false' + showText='false' + locales='{java.util.Arrays.asList(org.nuiton.i18n.I18n.getLoader().getLocales())}' + selectedLocale='{config.getLocale()}'/> + </JToolBar> + </JMenuBar> + + <ButtonGroup id='treeModelMode' + onStateChanged='handler.changeTreeModelMode(context, (TreeModelMode) treeModelMode.getSelectedValue())'/> + + <JToolBar id='treeToolBar'> + <JToggleButton id='toTreeMode'/> + <JToggleButton id='toFlatMode'/> + <JSeparator orientation='vertical'/> + <javax.swing.Box.Filler constructorParams='SwingUtil.newMinDimension(), SwingUtil.newMinDimension(), SwingUtil.newMaxXDimension()'/> + <JButton id='collapseAll' onActionPerformed='collapseAll((PropertieNode)tree.getSelectionValue())'/> + <JButton id='expandAll' onActionPerformed='expandAll((PropertieNode)tree.getSelectionValue())'/> + </JToolBar> + + <PropertiesTreeModel id='treeModel' mode='{context.getTreeModelMode()}'/> + + <javax.swing.tree.DefaultTreeSelectionModel id='treeSelectionModel'/> + <!--onValueChanged='updateSelectedKey((PropertieNode) tree.getSelectionValue())'/--> + + <JSplitPane id='splitpane' constraints="BorderLayout.CENTER"> + + <!-- left : bundles keys --> + + <!--JScrollPane id='keysView' columnHeaderView='{treeToolBar}'--> + <JScrollPane id='keysView'> + <JTree id="tree" constructorParams='treeModel' + onKeyReleased='updateSelectedKey((PropertieNode) tree.getSelectionValue())' + onMouseClicked='updateSelectedKey((PropertieNode) tree.getSelectionValue())' + cellRenderer='{ + new DefaultTreeCellRenderer() { + private static final long serialVersionUID=1L; + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { + PropertieNode n = (PropertieNode) value; + if (n != null) { + String text = n.toString(); + switch(getTreeModel().getMode()) { + case TREE: + if (!n.isRoot()) { + int lastSeparator = text.lastIndexOf("."); + text = text.substring(lastSeparator + 1); + } + break; + case FLAT: + break; + } + value = text; + } + return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); + } + + } + }'/> + </JScrollPane> + + + <!-- right : bundles values (for the selected key) --> + + <!--JScrollPane id='valuesView' horizontalScrollBarPolicy="horizontal_scrollbar_never"--> + <JScrollPane id='valuesView'> + <JPanel id="values" layout='{new GridLayout(0,1)}'/> + </JScrollPane> + + </JSplitPane> + + <!-- status message bar --> + <jaxx.runtime.swing.StatusMessagePanel id='status' + _help='"ui.main.statusBar"' + constraints="BorderLayout.SOUTH"/> + +</JFrame> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUIHandler.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUIHandler.java new file mode 100644 index 0000000..33d1305 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/I18nEditorUIHandler.java @@ -0,0 +1,937 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.ui; + +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import javax.swing.JMenu; +import org.nuiton.i18n.editor.*; +import java.awt.Desktop; +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import jaxx.runtime.DefaultApplicationContext.AutoLoad; +import jaxx.runtime.JAXXContext; +import jaxx.runtime.swing.AboutPanel; +import jaxx.runtime.swing.editor.config.ConfigUI; +import jaxx.runtime.swing.editor.config.ConfigUIBuilder; +import jaxx.runtime.swing.editor.config.model.ConfigUIModel; +import jaxx.runtime.swing.wizard.WizardUILancher; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.I18n; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.project.I18nProjectFactory; +import org.nuiton.i18n.editor.project.I18nProjectProvider; +import org.nuiton.i18n.editor.ui.project.ProjectStep; +import org.nuiton.i18n.editor.ui.project.ProjectUI; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; +import static org.nuiton.i18n.I18n._; +import static org.nuiton.i18n.I18n.n_; + +/** + * + * @author chemit + */ +@AutoLoad +public class I18nEditorUIHandler { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(I18nEditorUIHandler.class); + + /** + * Methode pour initialiser l'ui principale sans l'afficher. + * + * Si un projet est charge dans le context, alors on le charge dans l'ui. + * + * @param context le context applicatif + * @param fullscreen flag pour indiquer si on doit ouvrir l'ui en model console (pleine ecran). + * @return l'ui instancie et initialisee mais non visible encore + */ + public I18nEditorUI initUI(I18nEditorContext context, boolean fullscreen) { + + final I18nEditorUI ui = new I18nEditorUI(context); + + I18nEditorContext.MAIN_UI_ENTRY_DEF.setContextValue(context, ui); + + // set fullscreen propery on main ui + ui.getGraphicsConfiguration().getDevice().setFullScreenWindow(fullscreen ? ui : null); + + // chargement du menu des projets existants + context.firePropertyChange("projects", null, context.getConfig().getProjects()); + + I18nProject project = context.getProject(); + if (project != null) { + + openProjectUI(ui, project); + } + return ui; + } + + /** + * Permet de recharger l'ui principale et de changer de le mode d'affichage. + * + * @param context le contexte applicatif + */ + public void reloadUI(I18nEditorContext context) { + + // on conserve la clef actuellement selectionnee + context.saveSelectedKey(); + + // must remove all properties listener on config + context.getContextValue(I18nEditorConfig.class).removeJaxxPropertyChangeListener(); + context.removeJaxxPropertyChangeListener(); + + boolean fullscreen = context.getConfig().isFullScreen(); + // scan main ui + I18nEditorUI ui = getUI(context); + + + if (ui != null) { + + I18nEditorContext.MAIN_UI_ENTRY_DEF.removeContextValue(context); + + ui.dispose(); + + ui.setVisible(false); + } + + ui = initUI(context, fullscreen); + + // show ui + ui.setVisible(true); + } + + /** + * Creer un projet. + * + * @param context le context applicatif + */ + public void createProject(I18nEditorContext context) { + try { + createProjectUI(context); + + I18nProject project = context.getProject(); + + if (project == null) { + return; + } + + log.info("open project " + project); + + context.firePropertyChange(I18nEditorContext.PROJECT_PROPERTY, null, project); + context.firePropertyChange("projects", null, context.getConfig().getProjects()); + + } catch (Exception ex) { + throw new RuntimeException("could not open project for reason " + ex.getMessage(), ex); + } + } + + /** + * Ouvre un projet. + * + * @param context le context applicatif + */ + public void openOtherProject(I18nEditorContext context) { + try { + + File f = ProjectUIModel.chooseFile(getUI(context), + _("i18neditor.title.choose.project"), + _("i18neditor.action.choose.project"), + null, + "^.+\\.i18nproject$", + _("i18neditor.action.choose.project.description")); + if (f == null) { + return; + } + loadProject(context, f); + + I18nProject project = context.getProject(); + + if (project == null) { + return; + } + + log.info("open project " + project); + + context.firePropertyChange(I18nEditorContext.PROJECT_PROPERTY, null, project); + + } catch (Exception ex) { + throw new RuntimeException("could not open project for reason " + ex.getMessage(), ex); + } + } + + /** + * Ouvre un projet. + * + * @param context le context applicatif + * @param name le nom du projet a ouvrir + */ + public void openProject(I18nEditorContext context, String name) { + try { + File f = context.getConfig().getProjectStore(name); + loadProject(context, f); + + I18nProject project = context.getProject(); + + if (project == null) { + return; + } + + log.info("open project " + project); + + context.firePropertyChange(I18nEditorContext.PROJECT_PROPERTY, null, project); + + } catch (Exception ex) { + throw new RuntimeException("could not open project for reason " + ex.getMessage(), ex); + } + } + + /** + * Ferme un projet actuellement charge. + * + * @param context le context applicatif + * @param project le projet a fermer + */ + public void closeProject(I18nEditorContext context, I18nProject project) { + + if (project == null) { + return; + } + + log.info("close project " + project); + + I18nEditorContext.PROJECT_DEF.removeContextValue(context); + + context.firePropertyChange(I18nEditorContext.PROJECT_PROPERTY, project, null); + + context.setSelectedKey(null); + } + + /** + * Ferme l'application. + * + * @param context le context applicatif + */ + public void close(I18nEditorContext context) { + log.info("I18nEditor quitting..."); + I18nEditorUI ui = getUI(context); + boolean canContinue = ensureModification(ui); + if (!canContinue) { + return; + } + try { + if (ui != null) { + ui.dispose(); + } + } finally { + System.exit(0); + } + } + + /** + * Méthode pour changer de mode d'affichage. + * <p/> + * Si <code>fullscreen</code> est à <code>true</code> alors on passe en + * mode console (c'est à dire en mode plein écran exclusif), sinon on + * passe en mode fenetré normal. + * + * @param context le context applicatif + * @param fullscreen le nouvel état requis. + */ + public void changeScreen(I18nEditorContext context, boolean fullscreen) { + I18nEditorUI ui = getUI(context); + boolean canContinue = ensureModification(ui); + if (!canContinue) { + return; + } + context.getConfig().setFullscreen(fullscreen); + + // on recharge l'ui + reloadUI(context); + } + + /** + * Methode pour changer la langue utilisee. + * + * Cette action recharge le systeme i18n avec la nouvelle locale + * donnee puis recherge les interfaces graphiques. + * + * @param context applicatif + * @param newLocale la nouvelle locale a utilisee dans l'application. + */ + public void changeLanguage(I18nEditorContext context, Locale newLocale) { + if (newLocale == null) { + // pour eviter du code reentrant due aux binding... + return; + } + I18nEditorUI ui = getUI(context); + + boolean canContinue = ensureModification(ui); + if (!canContinue) { + return; + } + I18nEditorConfig config = context.getConfig(); + Locale oldLocale = config.getLocale(); + if (oldLocale != null && oldLocale.equals(newLocale)) { + return; + } + log.info("new locale to use " + newLocale + " (old: " + oldLocale + ")"); + // sauvegarde de la nouvelle locale + config.setLocale(newLocale); + + // chargement de la nouvelle locale dans le système i18n + I18n.init(newLocale); + + // on recharge l'ui + reloadUI(context); + } + + /** + * Methode pour changer le type de modele de l'arbre de l'arbre des clefs. + * + * @param context le contexte applicatif + * @param newMode le nouveau mode du modele d'arbre + */ + public void changeTreeModelMode(I18nEditorContext context, TreeModelMode newMode) { + //I18nEditorContext context = ui.getContext(); + + TreeModelMode oldMode = context.getTreeModelMode(); + if (oldMode == newMode) { + // on evite le code re-entrant + return; + } + + log.info("new tree model mode : " + newMode); + + // on conserve la clef actuellement selectionnee + context.saveSelectedKey(); + + // on change le type de model d'arbre (cela reconstruira l'arbre) + context.setTreeModelMode(newMode); + } + + public void updateProjectsMenuUI(final I18nEditorContext context) { + I18nEditorUI ui = getUI(context); + if (ui == null) { + return; + } + JMenu menu = ui.getMenuFileOpenProject(); + + Component[] components = menu.getMenuComponents(); + for (Component c : components) { + if (c instanceof JMenuItem && ((JComponent) c).getClientProperty("doNotRemove") == null) { + menu.remove(c); + } + } + Action action = new AbstractAction() { + + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed(ActionEvent e) { + JMenuItem source = (JMenuItem) e.getSource(); + String name = source.getName(); + openProject(context, name); + } + }; + List<String> projects = new ArrayList<String>(context.getConfig().getProjects()); + Collections.sort(projects); + for (String name : projects) { + JMenuItem item = new JMenuItem(action); + item.setName(name); + item.setText(name); + menu.add(item); + } + ui.getMenuFileDeleteProject().setEnabled(!projects.isEmpty()); + + } + + public void updateSelectedValue(I18nEditorContext context, String packageName, Locale locale, String key, String newValue) { + I18nProject project = context.getProject(); + if (log.isDebugEnabled()) { + log.debug(packageName + " - " + locale + " - " + key + " = " + newValue); + } + if (project != null) { + project.updateValue(packageName, locale, key, newValue); + } + } + + public void deleteSelectedValueForLocale(I18nEditorContext context, BundleValueUI sourceUI, String packageName, Locale locale, String key) { + I18nProject project = context.getProject(); + if (log.isDebugEnabled()) { + log.debug(packageName + " - " + locale + " - " + key); + } + if (project != null) { + project.deleteKey(packageName, locale, key); + } + + final BundleValuesUI ui = sourceUI.getParentContainer(BundleValuesUI.class); + ui.getBundlesContent().remove(sourceUI); + if (ui.getBundlesContent().getComponentCount() == 0) { + Container parent = ui.getParent(); + // plus de bundle pour le paquetage + parent.remove(ui); + getUI(context).getValues().repaint(); + // on doit recharger les clefs + + context.saveSelectedKey(); + + reloadKeys(getUI(context)); + } else { + ui.revalidate(); + } + } + + /** + * Création d'un nouveau bundle utilistateur + * + * @param context + */ + public void createBundle(I18nEditorContext context) { +// // Ouverture de la fenêtre de création +// CreateBundleUI createBundle = new CreateBundleUI(ui); +// createBundle.setVisible(true); +// +// String bundle = createBundle.getBundle(); +// +// if (bundle != null) { +// // Ajout du nouveau bundle à l'écran et dans le menu +// +//// I18nEditorContext context = ui.getContext(); +// //List<String> bundlesI18n = context.getBundlesI18n(); +// createBundleChekBoxMenu(ui, bundle); +// //bundlesI18n.add(bundle); +// reloadValues(ui); +// } + } + + public void createBundle(I18nEditorContext context, String bundle) { +// // Ouverture de la fenêtre de création +// CreateBundleUI createBundle = new CreateBundleUI(ui); +// createBundle.setVisible(true); +// +// String bundle = createBundle.getBundle(); +// +// if (bundle != null) { +// // Ajout du nouveau bundle à l'écran et dans le menu +// +//// I18nEditorContext context = ui.getContext(); +// //List<String> bundlesI18n = context.getBundlesI18n(); +// createBundleChekBoxMenu(ui, bundle); +// //bundlesI18n.add(bundle); +// reloadValues(ui); +// } + } + + /** + * Création d'un nouveau bundle utilistateur + * + * @param context + */ + public void createPackage(I18nEditorContext context) { +// // Ouverture de la fenêtre de création +// CreatePackageUI createBundle = new CreatePackageUI(ui); +// createBundle.setVisible(true); +// +// String bundle = createBundle.getBundle(); +// +// if (bundle != null) { +// // Ajout du nouveau bundle à l'écran et dans le menu +// +//// I18nEditorContext context = ui.getContext(); +//// List<String> bundlesI18n = context.getBundlesI18n(); +// createPackageChekBoxMenu(ui, bundle); +// //bundlesI18n.add(bundle); +// reloadKeys(ui); +// reloadValues(ui); +// } + } + + public void showConfig(I18nEditorContext context) { + I18nEditorUI ui = getUI(context); + + ConfigUIModel model = new ConfigUIModel(context.getConfig()); + model.addCategory( + n_("i18neditor.config.category.directories"), + n_("i18neditor.config.category.directories.description"), + I18nEditorConfig.Option.CONFIG_FILE, + I18nEditorConfig.Option.PROJECTS_DIRECTORY, + I18nEditorConfig.Option.TMP_DIRECTORY); + + model.addCategory( + n_("i18neditor.config.category.ui"), + n_("i18neditor.config.category.ui.description"), + I18nEditorConfig.Option.FULL_SCREEN, + I18nEditorConfig.Option.LOCALE); + + ConfigUI configUI = ConfigUIBuilder.newConfigUI(context, model, "i18neditor.config.category.ui"); + + ConfigUIBuilder.showConfigUI(configUI, ui, false); +// configUI.showInDialog(ui, ui != null); + } + + public void showHelp(JAXXContext context, String helpId) { +// ObserveMainUI mainUI = getUI(context); +// +// ObserveHelpBroker helpBroker = context.getContextValue(ObserveHelpBroker.class); +// +// if (mainUI == null) { +// log.info("no mainUI, open in autonome frame"); +// // ouvrir dans une fenetre dedie +// helpBroker.showHelpSet(); +// return; +// } +// +// JHelp ui = mainUI.getHelp(); +// +// if (helpId == null) { +// helpId = helpBroker.getDefaultID(); +// } +// log.debug("show help " + helpId); +// ui.setCurrentID(helpId); +// +// mainUI.setContextValue(mainUI.getMode(), "oldMode"); +// mainUI.setMode(ObserveUIMode.HELP); +// } +// +// public void closeHelp(JAXXContext context) { +// ObserveMainUI mainUI = getUI(context); +// ObserveUIMode oldMode = mainUI.getContextValue(ObserveUIMode.class, "oldMode"); +// if (oldMode == null) { +// // on regarde si une base est chargee +// ObserveDataContext dataContext = context.getContextValue(ObserveDataContext.class); +// StorageService<?> mainStorage = dataContext.getStorage(); +// +// if (mainStorage == null) { +// oldMode = ObserveUIMode.NO_DB; +// } else { +// oldMode = ObserveUIMode.DB; +// } +// } +// mainUI.setMode(oldMode); + } + + public void gotoSite(I18nEditorContext context) { + + I18nEditorConfig config = context.getConfig(); + + URL siteURL = config.getOptionAsURL("application.site.url"); + + log.info("goto " + siteURL); + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + try { + Desktop.getDesktop().browse(siteURL.toURI()); + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + } + } + } + + public void showAbout(I18nEditorContext context) { + + I18nEditorUI ui = getUI(context); + + AboutPanel about = new AboutPanel(ui); + about.setTitle(_("i18neditor.title.about")); + about.setAboutText(_("i18neditor.about.message")); + about.setBottomText(context.getConfig().getCopyrightText()); +// about.setIconPath("/icons/logo-OT_web.png"); + about.setLicenseFile("META-INF/nuiton-i18n-editor-LICENSE.txt"); + about.setThirdpartyFile("META-INF/nuiton-i18n-editor-THIRD-PARTY.txt"); + about.init(); + about.showInDialog(ui, true); + } + + public void showStorageInfo(I18nEditorContext context) { + + I18nEditorUI ui = getUI(context); + + String text = null; + if (context.getProject() == null) { + text = _("i18neditor.message.no.project.loaded"); + } else { + text = context.getProject().toString(); + } + JOptionPane.showMessageDialog( + ui, + text, + _("i18neditor.title.project.info"), + JOptionPane.INFORMATION_MESSAGE); + } + + /** + * Méthode pour lancer l'assistant de creation de projet. + * + * @param rootContext le context applicatif + */ + public void createProjectUI(final JAXXContext rootContext) { + boolean canContinue = ensureModification(rootContext); + if (!canContinue) { + return; + } + + final I18nEditorUI mainUI = getUI(rootContext); + + new WizardUILancher<ProjectStep, ProjectUIModel, ProjectUI>(rootContext, mainUI, ProjectUI.class, ProjectUIModel.class) { + + @Override + protected void init(ProjectUI ui) { + ProjectUIModel model = ui.getModel(); + Set<I18nProjectProvider<?>> providers = I18nProjectFactory.getProviders(); + if (!providers.isEmpty()) { + model.setType(providers.iterator().next()); + } + } + + @Override + protected void doAction(ProjectUI ui) { + log.info(ui.getName()); + ProjectUIModel model = ui.getModel(); + + I18nProject project = null; + I18nProjectProvider<?> type = model.getType(); + project = I18nProjectFactory.newProject(type.getType(), model); + try { + if (project.isStoreProject()) { + // ajout du projet dans la liste des projets connus + rootContext.getContextValue(I18nEditorConfig.class).getProjects().add(project.getName()); + // on enregistre la definition du projet + project.saveDefinition(); + } + project.load(); + I18nEditorContext.PROJECT_DEF.setContextValue(rootContext, project); + } catch (IOException ex) { + log.error(ex.getMessage(), ex); + } + } + + @Override + protected void doCancel(ProjectUI ui) { + super.doCancel(ui); + + } + }.start(); + } + + /** + * Méthode pour lancer l'assistant de mise a jour de projet. + * + * @param context le context applicatif + */ + public void editProject(final I18nEditorContext context) { + boolean canContinue = ensureModification(context); + if (!canContinue) { + return; + } + + final I18nEditorUI mainUI = getUI(context); + + new WizardUILancher<ProjectStep, ProjectUIModel, ProjectUI>(context, mainUI, ProjectUI.class, ProjectUIModel.class) { + + @Override + protected void doAction(ProjectUI ui) { + log.info(ui.getName()); + ProjectUIModel model = ui.getModel(); + I18nProject project = null; + I18nProjectProvider<?> type = model.getType(); + project = I18nProjectFactory.newProject(type.getType(), model); + try { + if (project.isStoreProject()) { + if (!context.getConfig().getProjects().contains(project.getName())) { + // ajout du projet dans la liste des projets connus + context.getConfig().getProjects().add(project.getName()); + } + // on enregistre la definition du projet + project.saveDefinition(); + } + project.load(); + I18nEditorContext.PROJECT_DEF.setContextValue(context, project); + } catch (IOException ex) { + log.error(ex.getMessage(), ex); + } + } + + @Override + protected void doCancel(ProjectUI ui) { + super.doCancel(ui); + + } + }.start(); + } + + public void deleteProject(I18nEditorContext context) { + JComboBox box = new JComboBox(); + List<String> projects = new ArrayList<String>(context.getConfig().getProjects()); + Collections.sort(projects); + box.addItem(""); + for (String p : projects) { + box.addItem(p); + } + JOptionPane.showMessageDialog(getUI(context), box, _("i18neditor.choose.project.to.delete"), JOptionPane.INFORMATION_MESSAGE); + + String selectedProject = (String) box.getSelectedItem(); + if (!selectedProject.isEmpty()) { + File f = context.getConfig().getProjectStore(selectedProject); + f.delete(); + context.getConfig().getProjects().remove(selectedProject); + log.info("project ot delete : " + selectedProject); + context.firePropertyChange("projects", null, context.getConfig().getProjects()); + } + } + + /** + * Charger un projet. + * + * @param context le context applicatif + * @param projectFile le fichier de definition du projet a ouvrir + */ + public void loadProject(I18nEditorContext context, File projectFile) { + FileInputStream stream = null; + try { + I18nProject project = I18nProjectFactory.newProject(projectFile); + + project.setStoreFile(projectFile); + project.load(); + I18nEditorContext.PROJECT_DEF.setContextValue(context, project); + + } catch (Exception ex) { + throw new RuntimeException("could not load project for reason " + ex.getMessage(), ex); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException ex) { + throw new RuntimeException("could not close project for reason " + ex.getMessage(), ex); + } + } + } + } + + protected void openProjectUI(I18nEditorUI ui, I18nProject project) { + if (ui == null) { + return; + } + if (log.isDebugEnabled()) { + log.debug("opening project ui for " + project); + } + + List<Locale> selectedLocales = project.getSelectedBundles(); + List<String> selectedPackages = project.getSelectedPackages(); + JMenu menuPackages = ui.getMenuPackages(); + for (String packageName : project.getPackages()) { + boolean selected = selectedPackages.contains(packageName); + JCheckBoxMenuItem checkBoxMenuItem = new PackageCheckBoxMenuUI(ui, packageName, selected); + menuPackages.add(checkBoxMenuItem); + } + + JMenu menuBundles = ui.getMenuBundles(); + for (Locale l : project.getBundles()) { + boolean selected = selectedLocales.contains(l); + JCheckBoxMenuItem checkBoxMenuItem = new BundleCheckBoxMenuUI(ui, l, selected); + menuBundles.add(checkBoxMenuItem); + } + + // Initialisation des données + reloadKeys(ui); + } + + protected void closeProjectUI(final I18nEditorUI ui, I18nProject project) { + + if (ui == null) { + return; + } + + if (log.isDebugEnabled()) { + log.debug("closing project ui for " + project); + } + JMenu menu = ui.getMenuBundles(); + Component[] components = menu.getMenuComponents(); + for (Component c : components) { + if (c instanceof JCheckBoxMenuItem) { + menu.remove(c); + } + } + menu = ui.getMenuPackages(); + components = menu.getMenuComponents(); + for (Component c : components) { + if (c instanceof JCheckBoxMenuItem) { + menu.remove(c); + } + } + ui.getValues().removeAll(); + ui.getTreeModel().setKeys(new String[0]); + ui.getValues().repaint(); + } + + /** + * Recharge les clés dans l'arbre des propriétés en fonction des paquetages + * @param ui + */ + protected void reloadKeys(I18nEditorUI ui) { + I18nEditorContext context = ui.getContext(); + I18nProject project = context.getProject(); + if (project == null) { + return; + } + + // on supprime la selection (elle sera repositionnee ensuite si necessaire) + ui.getTreeSelectionModel().clearSelection(); + + // construction du nouveau model + String[] keys = project.getKeys(); + PropertiesTreeModel model = ui.getTreeModel(); + log.info("wil use " + keys.length + " keys"); + model.setKeys(keys); + ui.getTree().repaint(); + reSelectSelectedKey(ui); + } + + public void reSelectSelectedKey(I18nEditorUI ui) { + + I18nEditorContext context = ui.getContext(); + // recuperation de l'ancienne clef selectionne + String selectedKey = context.getSelectedKey(); + if (selectedKey == null) { + // on recherche dans une seconde entree du context + // positionne explicitement par le developpeur + // ceci est necessaire car le model est reconstruit a chaque + // nouvelle ui (changement de langue,...) et on perd donc l'information + // sur selectedKey due a un binding... + selectedKey = context.popSelectedKey(); + if (selectedKey != null && !ui.getTreeModel().containsKey(selectedKey)) { + selectedKey = null; + } + context.setSelectedKey(selectedKey); + } + log.info(selectedKey); + } + + /** + * Recharge les valeurs de la clef selectionnee dans l'arbre de navigation. + * + * @param ui + */ + protected void reloadValues(I18nEditorUI ui) { + String key = ui.getContext().getSelectedKey(); + reloadValues(ui, key); + } + + /** + * Recharge les valeurs d'une clef donnee. + * + * @param ui + * @param key la clef de traduction dont on va editer les traductions + */ + protected void reloadValues(I18nEditorUI ui, String key) { + I18nEditorContext context = ui.getContext(); + I18nProject project = context.getProject(); + if (project == null) { + return; + } + + JPanel valuesUI = ui.getValues(); + valuesUI.removeAll(); + + if (log.isDebugEnabled()) { + log.debug("for key : " + key); + } + + if (key != null) { + List<String> packagesI18n = project.getSelectedPackages(); + + for (String p : packagesI18n) { + Map<Locale, String> values = project.getValues(p, key); + if (values != null && !values.isEmpty()) { + BundleValuesUI bundleValuesUI = new BundleValuesUI(context); + bundleValuesUI.setPackageName(p); + bundleValuesUI.setKey(key); + if (log.isDebugEnabled()) { + log.debug("adding package " + p); + } + bundleValuesUI.setBundles(new ArrayList<Locale>(values.keySet())); + for (java.util.Map.Entry<Locale, String> e : values.entrySet()) { + BundleValueUI v = new BundleValueUI(bundleValuesUI); + v.setBundle(e.getKey()); + if (log.isDebugEnabled()) { + log.debug(" - adding locale " + e.getKey()); + } + v.setKey(key); + v.setPackageName(p); + v.setInitialValue(e.getValue()); + bundleValuesUI.getBundlesContent().add(v); + } + valuesUI.add(bundleValuesUI); + } + } + } + + JScrollPane valuesView = ui.getValuesView(); + valuesView.validate(); + valuesView.repaint(); + } + + /** + * Test if there is some modification on screen, + * + * @param rootContext the context + * @return <code>true</code> if no more modification is detected + * @throws IllegalArgumentException if rootContext is null + */ + protected boolean ensureModification(JAXXContext rootContext) throws IllegalArgumentException { + if (rootContext == null) { + throw new IllegalArgumentException("rootContext can not be null"); + } + I18nEditorUI ui = getUI(rootContext); + if (ui == null) { + // no ui, so no modification + return true; + } + return true; + } + + protected I18nEditorUI getUI(JAXXContext context) { + I18nEditorUI ui; + if (context instanceof I18nEditorUI) { + ui = (I18nEditorUI) context; + } else if (context instanceof I18nEditorContext) { + ui = ((I18nEditorContext) context).getMainUI(); + } else { + ui = I18nEditorContext.MAIN_UI_ENTRY_DEF.getContextValue(context); + } + return ui; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PackageCheckBoxMenuUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PackageCheckBoxMenuUI.jaxx new file mode 100644 index 0000000..be91774 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PackageCheckBoxMenuUI.jaxx @@ -0,0 +1,66 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JCheckBoxMenuItem id='content' + text='{updateText(getUrl())}' + toolTipText='{_("i18neditor.resource.name", getUrl())}' + onItemStateChanged='updateState(event)'> + + <!-- le nom du bundle --> + <String id='url' javaBean='""'/> + + <script><![CDATA[ +import org.nuiton.i18n.editor.*; +import java.util.Locale; +import jaxx.runtime.JAXXContext; + +public PackageCheckBoxMenuUI(JAXXContext parentContext, String bundle, boolean selected) { + super(null, selected); + setContextValue(parentContext); + setUrl(bundle); +} + +protected String updateText(String name) { + int index = name.lastIndexOf("/"); + if (index > -1) { + name = name.substring(index + 1); + } + return name; +} + +protected void updateState(ItemEvent e) { + I18nEditorContext context = getContextValue(I18nEditorContext.class); + if (context == null) { + // pas encore de context (ui en construction) + return; + } + if (e.getStateChange() == ItemEvent.SELECTED) { + context.addSelectedPackage(url); + } else { + context.removeSelectedPackage(url); + } +} +]]> + </script> + +</JCheckBoxMenuItem> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertieNode.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertieNode.java new file mode 100644 index 0000000..3e0faef --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertieNode.java @@ -0,0 +1,122 @@ +/* + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* */ +package org.nuiton.i18n.editor.ui; + +import javax.swing.tree.DefaultMutableTreeNode; +import java.io.Serializable; +import javax.swing.tree.MutableTreeNode; + +/** + * Représente un noeud de l'ardre des propriétés affichées. Le noeud contient + * le chemin de la propriétés, par exemple tutu.toto.tata va être représenté avec + * 3 noeuds avec comme chemin tutu, tutu.toto, tutu.toto.tata + * + * @author julien + */ +public class PropertieNode extends DefaultMutableTreeNode implements Comparable<PropertieNode> { + + /** @see Serializable */ + private static final long serialVersionUID = 1L; + + /** + * Constructeur avec l'objet contenu dans le noeud + * + * @param userObject chemin de la propriété + */ + public PropertieNode(Object userObject) { + super(userObject); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof PropertieNode) { + // Comparaison du chemin + PropertieNode node = (PropertieNode) obj; + return getUserObject().equals(node.getUserObject()); + } + return false; + } + + public PropertieNode getChildAt(String path) { + if (!isLeaf()) { + for (Object o : children) { + PropertieNode n = (PropertieNode) o; + if (path.equals(n.getUserObject())) { + return n; + } + } + } + return null; + } + + /** + * Ajoute le noeud fils a la bonne position (on conserve l'ordre alphabetique des path) + * + * Note : si le path existe deja, alors on ne fait rien. + * + * @param newChild le fils a ajouter + */ + @Override + public void add(MutableTreeNode newChild) { + int index = 0; + if (!isLeaf()) { + // on recherche la position ou ajouter la clef + for (Object o : children) { + PropertieNode n = (PropertieNode) o; + int compareTo = n.compareTo((PropertieNode) newChild); + if (compareTo == 0) { + // le noeud existe deja + return; + } + if (compareTo > 0) { + // le noeud courant doit etre apres le noeud a ajouter + break; + } + index++; + } + } + insert(newChild, index); + } + +// @Override +// public String toString() { +// String result = getUserObject().toString(); +// if (isRoot()) { +// // Si c'est la racine +// return result; +// } else { +// // Si c'est un chemin +// int lastSeparator = result.lastIndexOf("."); +// return result.substring(lastSeparator + 1); +// } +// } + + @Override + public int hashCode() { + return getUserObject().hashCode(); + } + + @Override + public int compareTo(PropertieNode node) { + // Compare les chemins + return getUserObject().toString().compareTo(node.getUserObject().toString()); + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertiesTreeModel.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertiesTreeModel.java new file mode 100644 index 0000000..c69286b --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/PropertiesTreeModel.java @@ -0,0 +1,199 @@ +/* + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* */ +package org.nuiton.i18n.editor.ui; + +import javax.swing.tree.DefaultTreeModel; +import java.io.Serializable; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.editor.I18nEditorContext; +import static org.nuiton.i18n.I18n._; + +/** + * Modèle de l'arbre des propriétés. Le modèle permet de sructurer les propriétés + * (ex: tutu.toto.tata) en un des noeuds de l'arbre. + * + * @author julien + */ +public class PropertiesTreeModel extends DefaultTreeModel { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(PropertiesTreeModel.class); + /** @see Serializable */ + private static final long serialVersionUID = 1L; + protected TreeModelMode mode; + protected String[] keys; + + /** + * Constructeur avec un liste de proprétés + * + * @param list liste de propriétés + */ + public PropertiesTreeModel(String... list) { + this(TreeModelMode.TREE, list); + } + + /** + * Constructeur avec un liste de proprétés + * + * @param mode le mode de modele a construire + * @param list liste de propriétés + */ + public PropertiesTreeModel(TreeModelMode mode, String... list) { + super(null); + this.keys = list; + + // Création de la racine (qui n'est jamais affichee) + PropertieNode r = new PropertieNode(""); +// PropertieNode r = new PropertieNode(list.length == 0 ? _("i18neditor.no.keys") : I18nEditorContext.ROOT_NAME); + setRoot(r); + setMode(mode); + } + + @Override + public PropertieNode getRoot() { + return (PropertieNode) super.getRoot(); + } + + public String[] getKeys() { + return keys; + } + + public void setKeys(String[] keys) { + this.keys = keys; + // on reconstruit le model + TreeModelMode theMode = mode; + mode = null; + setMode(theMode); + } + + public boolean containsKey(String selectedKey) { + if (keys != null) { + switch (mode) { + case TREE: + for (String k : keys) { + if (k.equals(selectedKey) || k.startsWith(selectedKey + ".")) { + return true; + } + } + break; + case FLAT: + + for (String k : keys) { + if (k.equals(selectedKey)) { + return true; + } + } + break; + } + } + return false; + } + + public PropertieNode getNode(String path) { + PropertieNode result = null; + switch (mode) { + case TREE: + String[] names = path.split("\\" + I18nEditorContext.SEPARATOR); + result = getRoot(); + String currentPath = ""; + for (String name : names) { + if (!currentPath.isEmpty()) { + currentPath += I18nEditorContext.SEPARATOR; + } + currentPath += name; + result = result.getChildAt(currentPath); + } + break; + case FLAT: + result = getRoot().getChildAt(path); + break; + } + return result; + } + + public TreeModelMode getMode() { + return mode; + } + + public void setMode(TreeModelMode mode) { + this.mode = mode; + + if (root == null) { + return; + } + PropertieNode r = (PropertieNode) root; + if (!r.isLeaf()) { + // on supprime tous les fils + r.removeAllChildren(); + } + if (keys == null || keys.length == 0) { + // pas de clef a reconstruire + PropertieNode r2 = new PropertieNode(_("i18neditor.no.keys")); + r.add(r2); + // on notifie que le modele a ete reconstruit + nodeStructureChanged(root); + return; + } + + // on reconstruit le modele + + switch (mode) { + case TREE: + buildTreeModel(r, keys); + break; + + case FLAT: + buildFlatModel(r, keys); + break; + } + // on notifie que le modele a ete reconstruit + nodeStructureChanged(root); + } + + private void buildTreeModel(PropertieNode r, String[] list) { + for (String property : list) { + String path = ""; + PropertieNode parent = r; + + String[] names = property.split("\\" + I18nEditorContext.SEPARATOR); + for (String name : names) { + if (!path.isEmpty()) { + + path += I18nEditorContext.SEPARATOR; + } + path += name; + + PropertieNode node = parent.getChildAt(path); + if (node == null) { + node = new PropertieNode(path); + parent.add(node); + } + parent = node; + } + } + } + + private void buildFlatModel(PropertieNode r, String[] list) { + + for (String property : list) { + PropertieNode node = new PropertieNode(property); + r.add(node); + } + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/TreeModelMode.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/TreeModelMode.java new file mode 100644 index 0000000..13495d4 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/TreeModelMode.java @@ -0,0 +1,36 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.ui; + +/** + * Pour caracteriser le mode a utiliser dans le modele de navigation. + * + * @author chemit + */ +public enum TreeModelMode { + + /** + * pour afficher les clefs sous forme d'arbre + */ + TREE, + /** + * pour afficher les clefs a plat + */ + FLAT +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectStep.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectStep.java new file mode 100644 index 0000000..c786adf --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectStep.java @@ -0,0 +1,67 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.ui.project; + +import static org.nuiton.i18n.I18n.n_; + +/** + * Pour caracteriser les étapes lors du wizard de creation de projet + * + * @author tchemit + */ +public enum ProjectStep implements jaxx.runtime.swing.wizard.WizardStep { + + /** + * pour choisir le type de projet a creer (@see ProjectType) + */ + CHOOSE_PROJECT_TYPE(n_("i18neditor.project.step.chooseProjectType"), n_("i18neditor.project.step.chooseProjectType.description")), + /** + * pour configurer un projet + */ + CONFIGURE_PROJECT(n_("i18neditor.project.step.configureProject"), n_("i18neditor.project.step.configureProject.description")), + /** + * pour choisir les bundles parmis ceux trouvés + */ + SELECT_BUNDLES(n_("i18neditor.project.step.selecteBundles"), n_("i18neditor.project.step.selecteBundles.description")), + /** + * pour renseigner si on persiste le projet + */ + PERSIST(n_("i18neditor.project.step.persist"), n_("i18neditor.project.step.persist.description")), + /** + * pour confirmer et charger le projet + */ + RESUME(n_("i18neditor.project.step.resume"), n_("i18neditor.project.step.resume.description")); + private final String label; + private final String description; + + private ProjectStep(String label, String description) { + this.label = label; + this.description = description; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getDescription() { + return description; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.css new file mode 100644 index 0000000..8a24599 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.css @@ -0,0 +1,59 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +#mainUI { + title:"i18neditor.title.noproject"; + modal:true; + resizable:false; + defaultCloseOperation:"dispose_on_close"; + layout:{new BorderLayout()}; +} + +#tabs { + border:{BorderFactory.createEmptyBorder(6,6,6,6)}; +} + +#cancelAction { + text:"i18neditor.action.cancel"; + toolTipText:"i18neditor.action.cancel.tip"; + actionIcon:"cancel"; +} + +#previousAction { + text:"i18neditor.action.goto.previous.stage"; + toolTipText:"i18neditor.action.goto.previous.stage.tip"; + actionIcon:"previous-step"; + enabled:{getModel().getPreviousStep() != null}; +} + +#nextAction { + text:"i18neditor.action.goto.next.stage"; + toolTipText:"i18neditor.action.goto.next.stage.tip"; + actionIcon:"next-step"; + enabled:{getModel().isValidStep()}; + visible:{getModel().getNextStep() != null}; +} + +#applyAction { + text:"i18neditor.action.apply"; + toolTipText:"i18neditor.action.apply.tip"; + actionIcon:"accept"; + enabled:{getModel().isValidStep()}; + visible:{getModel().getNextStep() == null}; +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.jaxx new file mode 100644 index 0000000..fae2fed --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUI.jaxx @@ -0,0 +1,299 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JDialog id="mainUI" + implements='jaxx.runtime.swing.wizard.WizardUI<ProjectStep, ProjectUIModel>' + width='550' height='560' defaultCloseOperation='dispose_on_close'> + + <style source='ProjectUI.css'/> + + <script><![CDATA[ +import javax.swing.table.DefaultTableCellRenderer; + +import jaxx.runtime.JAXXContext; +import jaxx.runtime.SwingUtil; +import jaxx.runtime.swing.MyDefaultCellEditor; +import jaxx.runtime.swing.wizard.WizardOperationState; +import jaxx.runtime.swing.wizard.WizardUtil; +import static jaxx.runtime.JAXXContextEntryDef.newDef; +import static jaxx.runtime.Util.checkJAXXContextEntry; + +import org.nuiton.i18n.editor.I18nEditorContext; +import org.nuiton.i18n.editor.I18nEditorConfig; +import org.nuiton.i18n.editor.ui.I18nEditorUIHandler; +import org.nuiton.i18n.editor.ui.project.ProjectStep; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; +import org.nuiton.i18n.editor.ui.project.tabs.*; +import static org.nuiton.i18n.I18n.n_; + +protected I18nEditorUIHandler handler = getContextValue(I18nEditorUIHandler.class); +protected I18nEditorConfig config = getContextValue(I18nEditorConfig.class); +protected I18nEditorContext context = getContextValue(I18nEditorContext.class); +protected ProjectUIModel model = getContextValue(ProjectUIModel.class); + + +private boolean contextInitialized; + +public ProjectUI(Window owner, JAXXContext parentContext) { + super(owner); + + // verification du context parent + checkJAXXContextEntry(parentContext, newDef(ProjectUIModel.class)); + checkJAXXContextEntry(parentContext, newDef("apply", Runnable.class)); + checkJAXXContextEntry(parentContext, newDef("cancel", Runnable.class)); + + if (owner != null) { + setContextValue(owner, "parent"); + } + if (parentContext instanceof jaxx.runtime.JAXXInitialContext) { + ((jaxx.runtime.JAXXInitialContext)parentContext).to(this); + } else { + setContextValue(parentContext); + } + contextInitialized = true; +} + +@Override +public ProjectUIModel getModel() { + return model; +} + +@Override +public void start() { + + // on demarre le modele + getModel().start(); + + // centrage sur la frame parent + SwingUtil.center(getContextValue(Window.class, "parent"), this); + + // affichage ui + setVisible(true); +} + +@Override +public ProjectStep getSelectedStep() { + int index = tabs.getSelectedIndex(); + AbstractProjectTabPanelUI c = null; + if (index > -1) { + c = (AbstractProjectTabPanelUI) tabs.getComponentAt(index); + } + ProjectStep result = c == null ? null : c.getStep(); + return result; +} + +@Override +public AbstractProjectTabPanelUI getStepUI(ProjectStep step) { + if (step != null) { + return (AbstractProjectTabPanelUI) getObjectById(step.name()); + } + return null; +} + +@Override +public AbstractProjectTabPanelUI getStepUI(int stepIndex) { + if (stepIndex > tabs.getTabCount()) { + return null; + } + return (AbstractProjectTabPanelUI) tabs.getComponentAt(stepIndex); +} + +@Override +public AbstractProjectTabPanelUI getSelectedStepUI() { + ProjectStep step = getSelectedStep(); + AbstractProjectTabPanelUI ui = getStepUI(step); + return ui; +} + +@Override +public void onWasStarted() { +} + +@Override +public void onModelStateChanged(WizardOperationState newState) { +} + +@Override +public void onOperationStateChanged(ProjectStep step, WizardOperationState newState) { + // mettre a jour l'onglet +} + +@Override +public void onStepChanged(ProjectStep newStep) { + if (newStep == ProjectStep.SELECT_BUNDLES) { + // recalcule des urls disponibles + SELECT_BUNDLES.getBundlesModel().setUrls(model.detectBundles()); + if (!model.isCreate()) { + // on reselectionne les urls du projet + SELECT_BUNDLES.getBundlesModel().setSelectedUrls(model.getProject().getUrls()); + } + return; + } +} + +@Override +public void onStepsChanged(ProjectStep[] steps) { + +} + +public I18nEditorUIHandler getHandler() { + return handler; +} + +public void apply() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + Runnable action = getContextValue(Runnable.class, "apply"); + action.run(); + } catch (Exception e) { + jaxx.runtime.swing.ErrorDialogUI.showError(e); + log.error(e.getMessage(), e); + } finally { + dispose(); + } + } + }); +} + +public void cancel() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + Runnable action = getContextValue(Runnable.class, "cancel"); + action.run(); + } catch (Exception e) { + jaxx.runtime.swing.ErrorDialogUI.showError(e); + log.error(e.getMessage(), e); + } finally { + dispose(); + } + } + }); +} + +void $afterCompleteSetup() { + + // installation du dispatcher de modifications du modele + WizardUtil.installWizardUIListeners(this); + + // ajout de la politique d'affichage des onglets + WizardUtil.addTabsDisplayUntilStepListener(this); + + // tableau de la synchronisation des données des references obsoletes + + final JTable table = SELECT_BUNDLES.getBundles(); + table.setRowHeight(24); + SwingUtil.fixTableColumnWidth(table, 0, 20); + + SwingUtil.setI18nTableHeaderRenderer(table, + n_("i18neditor.createProject.table.bundles.select"), + n_("i18neditor.createProject.table.bundles.select.tip"), + n_("i18neditor.createProject.table.bundles.url"), + n_("i18neditor.createProject.table.bundles.url.tip")); + + DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); + + DefaultTableCellRenderer renderer2 = new DefaultTableCellRenderer() { + private static final long serialVersionUID = 1L; + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + int modelColumn = table.convertColumnIndexToModel(column); + int modelRow = table.convertRowIndexToModel(row); + SelectBundlesTableModel model = (SelectBundlesTableModel) table.getModel(); + String text = model.getTextValueAt(modelRow, modelColumn); + JComponent c = (JComponent) super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column); + return c; + } + }; + + SwingUtil.setTableColumnRenderer(table, 0, SwingUtil.newBooleanTableCellRenderer(renderer)); + SwingUtil.setTableColumnRenderer(table, 1, SwingUtil.newStringTableCellRenderer(renderer2, 100, true)); + SwingUtil.setTableColumnEditor(table, 0, MyDefaultCellEditor.newBooleanEditor(false)); + + // pour tout selectionner - deselectionner dans l'entete du tableau + table.getTableHeader().addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + + int colIndex = table.getTableHeader().columnAtPoint(e.getPoint()); + colIndex = table.convertColumnIndexToModel(colIndex); + if (colIndex == 0) { + SelectBundlesTableModel model = (SelectBundlesTableModel) table.getModel(); + boolean oldValue = model.isSelectAll(); + // toggle selectAll + model.setSelectAll(!oldValue); + } + } + }); + + // chargement du modèle + getModel().finalizeUIInit(this); +} +]]> + </script> + + <CardLayout> + <!-- les differents contenu d'onglets --> + <ChooseProjectTypePanelUI id='CHOOSE_PROJECT_TYPE' constructorParams='this'/> + <ConfigureProjectPanelUI id='CONFIGURE_PROJECT' constructorParams='this'/> + <SelectBundlesPanelUI id='SELECT_BUNDLES' constructorParams='this'/> + <PersistPanelUI id='PERSIST' constructorParams='this'/> + <ResumePanelUI id='RESUME' constructorParams='this'/> + </CardLayout> + + <!-- les onglets --> + <JTabbedPane id='tabs' + constraints='BorderLayout.CENTER' + onStateChanged='getModel().gotoStep(getSelectedStep())'/> + + <!-- les actions --> + <Table weightx='1' fill='both' constraints='BorderLayout.SOUTH'> + <row> + <cell weightx='0.5' fill="both"> + <!-- pour annuler --> + <JButton id="cancelAction" + onActionPerformed='cancel()'/> + </cell> + <cell weightx='0.5' fill="both"> + <!-- pour aller sur l'onglet précédent --> + <JButton id="previousAction" + onActionPerformed='getModel().gotoPreviousStep()'/> + </cell> + <cell weightx='0.5' fill="both"> + <!-- pour aller sur l'onglet suivant --> + <JButton id="nextAction" + onActionPerformed='getModel().gotoNextStep()'/> + </cell> + <cell weightx='0.5' fill="both"> + <!-- pour apliquer la configuration --> + <JButton id="applyAction" + onActionPerformed='apply()'/> + </cell> + </row> + </Table> + +</JDialog> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUIModel.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUIModel.java new file mode 100644 index 0000000..1219894 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/ProjectUIModel.java @@ -0,0 +1,301 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.ui.project; + +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import jaxx.runtime.swing.wizard.WizardModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.i18n.editor.I18nEditorConfig; +import org.nuiton.i18n.editor.I18nEditorContext; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.project.I18nProjectFactory; +import org.nuiton.i18n.editor.project.I18nProjectProvider; +import org.nuiton.util.FileUtil; + +/** + * Le modele de l'ui pour creer un projet via un assistant + * + * @author chemit + */ +public class ProjectUIModel extends WizardModel<ProjectStep> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(ProjectUIModel.class); + public static final String TYPE_PROPERTY_NAME = "type"; + public static final String PROJECT_PROPERTY_NAME = "project"; + public static final String VALID_PROPERTY_NAME = "valid"; + /** le dictionnaire des differents projects possible indexes par leur provideur */ + protected Map<I18nProjectProvider<?>, I18nProject> projects; + /** Le provideur du projet en cours d'edition */ + protected I18nProjectProvider<?> type; + /** le projet en cours d'edition */ + protected I18nProject project; + /** la configuration de l'application */ + protected I18nEditorConfig config; + /** un drapeau pour savoir si on est en mode creation ou pas */ + protected boolean create; + + public ProjectUIModel() { + super(ProjectStep.class, + ProjectStep.CHOOSE_PROJECT_TYPE, + ProjectStep.CONFIGURE_PROJECT, + ProjectStep.SELECT_BUNDLES, + ProjectStep.PERSIST, + ProjectStep.RESUME); + this.projects = new HashMap<I18nProjectProvider<?>, I18nProject>(); + // init map of different types of projects + Set<I18nProjectProvider<?>> providers = I18nProjectFactory.getProviders(); + for (I18nProjectProvider<?> p : providers) { + I18nProject newProject = p.newProject(); + this.projects.put(p, newProject); + // the model listens every modification of each project + // and at each time revalidate the model + newProject.addPropertyChangeListener(new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + validate(); + } + }); + } + if (!providers.isEmpty()) { + setType(providers.iterator().next()); + } + } + + /** + * Methode pour finir l'initialisation de l'ui partir du modele. + * + * @param ui l'ui de l'assistant + */ + public void finalizeUIInit(final ProjectUI ui) { + + config = ui.getContextValue(I18nEditorConfig.class); + + I18nEditorContext ctxt = ui.getContextValue(I18nEditorContext.class); + + //TODO instead of seeking an ui model, must seek fro an incoming project... + I18nProject incomingProject = ctxt.getProject(); + + setCreate(incomingProject == null); + + if (incomingProject != null) { + + log.info("from a incoming project " + incomingProject); + if (log.isDebugEnabled()) { + log.debug("from a incoming project " + incomingProject); + } + I18nProjectProvider<?> provider = I18nProjectFactory.getProvider(incomingProject.getClass()); + setType(provider); + incomingProject.copyDefinitionTo(project); + } + } + + @Override + public void start() { + super.start(); + firePropertyChange(TYPE_PROPERTY_NAME, null, type); + firePropertyChange(PROJECT_PROPERTY_NAME, null, project); + if (!create && type != null && project != null) { + // ask to provider to fire every thing on the project ? + //TODO no! the project fire will do it in each specicialized project's ui + project.fireAllProperties(); + } + } + + @Override + public void updateUniverse() { + if (type == null) { + // pas de mode choisi donc l'univers ne change pas + return; + } + List<ProjectStep> universe = new ArrayList<ProjectStep>(); + universe.add(ProjectStep.CHOOSE_PROJECT_TYPE); + universe.add(ProjectStep.CONFIGURE_PROJECT); + + universe.add(ProjectStep.SELECT_BUNDLES); + universe.add(ProjectStep.PERSIST); + universe.add(ProjectStep.RESUME); + if (excludeSteps != null) { + universe.removeAll(excludeSteps); + } + setSteps(universe.toArray(new ProjectStep[universe.size()])); + } + + @Override + public boolean validate(ProjectStep s) { + boolean validate = super.validate(s); + if (validate) { + switch (s) { + case CHOOSE_PROJECT_TYPE: + validate = type != null; + break; + case CONFIGURE_PROJECT: + validate = type.validateUIModel(this); + break; + case SELECT_BUNDLES: + validate = !project.getUrls().isEmpty(); + break; + case PERSIST: + boolean storeProject = project.isStoreProject(); + if (storeProject) { + File storeFile = project.getStoreFile(); + if (create) { + validate = storeFile != null && !storeFile.exists(); + } else { + validate = storeFile != null && storeFile.exists(); + } + } + break; + case RESUME: +// validate = true; + break; + } + } + return validate; + } + + public I18nProjectProvider<?> getType() { + return type; + } + + public I18nProject getProject() { + return project; + } + + public <T extends I18nProject> T getProject(Class<T> projectClass) { + I18nProjectProvider<T> projectType = I18nProjectFactory.getProvider(projectClass); + if (!projects.containsKey(projectType)) { + throw new IllegalArgumentException(projectType + " is not a registred type"); + } + return (T) projects.get(projectType); + } + + public boolean isCreate() { + return create; + } + + public void setCreate(boolean create) { + this.create = create; + } + + public void setType(I18nProjectProvider<?> type) { + I18nProjectProvider<?> oldType = this.type; + this.type = type; + firePropertyChange(TYPE_PROPERTY_NAME, oldType, type); + I18nProject oldProject = this.project; + I18nProject newProject = projects.get(type); + this.project = newProject; + firePropertyChange(PROJECT_PROPERTY_NAME, oldProject, newProject); + validate(); + } + + /** + * Choisir un fichier via un sélecteur graphique de fichiers. + * + * @param parent le component swing appelant le controle + * @param title le titre du dialogue de sélection + * @param buttonLabel le label du boutton d'acceptation + * @param incoming le fichier de base à utilier + * @param filters les filtres + descriptions sur le sélecteur de fichiers + * @return le fichier choisi ou le fichier incoming si l'opération a été annulée + */ + public static File chooseFile(Component parent, String title, String buttonLabel, File incoming, String... filters) { + File oldBasedir = FileUtil.getCurrentDirectory(); + if (incoming != null) { + File basedir; + if (incoming.isFile()) { + basedir = incoming.getParentFile(); + } else { + basedir = incoming; + } + if (basedir.exists()) { + FileUtil.setCurrentDirectory(basedir); + } + } + File file = FileUtil.getFile(title, buttonLabel, parent, filters); + if (log.isDebugEnabled()) { + log.debug(title + " : " + file); + } + FileUtil.setCurrentDirectory(oldBasedir); + File result = file == null ? incoming : file; + return result; + } + + /** + * Choisir un répertoire via un sélecteur graphique de fichiers. + * + * @param parent le component swing appelant le controle + * @param title le titre de la boite de dialogue de sléection + * @param buttonLabel le label de l'action d'acceptation + * @param incoming le fichier de base à utiliser + * @return le répertoire choisi ou le répertoire incoming si l'opération a été annulée + */ + public static File chooseDirectory(Component parent, String title, String buttonLabel, File incoming) { + File oldBasedir = FileUtil.getCurrentDirectory(); + if (incoming != null) { + File basedir; + if (incoming.isFile()) { + basedir = incoming.getParentFile(); + } else { + basedir = incoming; + } + if (basedir.exists()) { + FileUtil.setCurrentDirectory(basedir); + } + } + String file = FileUtil.getDirectory(parent, title, buttonLabel); + if (log.isDebugEnabled()) { + log.debug(title + " : " + file); + } + FileUtil.setCurrentDirectory(oldBasedir); + return file == null ? incoming : new File(file); + } + + public List<URL> detectBundles() { + List<URL> urls; + try { + urls = type.detectBundles(this); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + // suppression du fichier pom.properties... + for (Iterator<URL> it = urls.iterator(); it.hasNext();) { + String path = it.next().toString(); + int index = path.lastIndexOf("/"); + path = path.substring(index + 1); + if (path.equals("pom.properties")) { + it.remove(); + } + } + return urls; + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/SelectBundlesTableModel.java b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/SelectBundlesTableModel.java new file mode 100644 index 0000000..61d3ad2 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/SelectBundlesTableModel.java @@ -0,0 +1,196 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ +package org.nuiton.i18n.editor.ui.project; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.swing.table.AbstractTableModel; +import org.nuiton.i18n.I18n; + +/** + * Le modèle pour la tableau dans l'import GPS qui contient les activités et + * les points gps calculés via le fichier gps importé. + * + * @author chemit + */ +public class SelectBundlesTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + protected static final String[] COLUMN_NAMES = { + I18n.n_("i18neditor.createproject.common.select"), + I18n.n_("i18neditor.createproject.url") + }; + protected static final Class<?>[] COLUMN_CLASSES = { + Boolean.class, + URL.class,}; + protected List<URL> entries; + protected Set<Integer> selected; + protected boolean selectAll; + + public SelectBundlesTableModel() { + super(); + selected = new java.util.HashSet<Integer>(); + } + + @Override + public Class<?> getColumnClass(int columnIndex) { + return COLUMN_CLASSES[columnIndex]; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 0; + } + + public void setUrls(List<URL> entries) { + // on nettoye toujours le model lors de l'init d'une entité + this.entries = new java.util.ArrayList<URL>(entries); +// selected.clear(); + // par defaut, on selectionne toutes les references + setSelectAll(true); + //fireTableDataChanged(); + } + + public List<URL> getSelectedUrls() { + List<URL> result = new ArrayList<URL>(); + for (Integer index : selected) { + result.add(entries.get(index)); + } + return result; + } + + public void setSelectedUrls(List<URL> selectedUrls) { + setSelectAll(false); + + for (URL selectedUrl : selectedUrls) { + // trouver l'inder de l'url + String selectedUrlStr = selectedUrl.toString(); + int index = 0; + for (URL u : entries) { + if (selectedUrlStr.equals(u.toString())) { + selected.add(index); + break; + } + index++; + } + } + } + + public int[] getSelected() { + int[] result = new int[selected.size()]; + int i = 0; + for (Integer index : selected) { + result[i++] = index; + } + return result; + } + + public boolean hasSelection() { + return !selected.isEmpty(); + } + + @Override + public int getRowCount() { + return entries == null ? 0 : entries.size(); + } + + @Override + public int getColumnCount() { + return COLUMN_CLASSES.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + Object value = null; + switch (columnIndex) { + case 0: + value = selected.contains(rowIndex); + break; + case 1: + value = getURLAt(rowIndex); + break; + default: + throw new IllegalStateException("can not get value for row " + rowIndex + ", col " + columnIndex); + } + return value; + } + + public String getTextValueAt(int rowIndex, int columnIndex) { + Object value = null; + + String text = ""; + URL p; + switch (columnIndex) { + case 0: + break; + case 1: + value = getURLAt(rowIndex); + String path = value.toString(); + int index = path.lastIndexOf("/"); + text = path.substring(index + 1) + "(" + path.substring(0, index) + ")"; + break; + default: + throw new IllegalStateException("can not get value for column " + columnIndex); + } + return text; + } + + public URL getURLAt(int rowIndex) { + return entries.get(rowIndex); + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (columnIndex == 0) { + Boolean value = (Boolean) aValue; + if (value) { + selected.add(rowIndex); + if (selected.size() == getRowCount()) { + selectAll = true; + } + } else { + selected.remove(rowIndex); + if (selected.isEmpty()) { + selectAll = false; + } + } + fireTableCellUpdated(rowIndex, columnIndex); + return; + } + + // no edit for others columns + } + + public boolean isSelectAll() { + return selectAll; + } + + public void setSelectAll(boolean selectAll) { + this.selectAll = selectAll; + selected.clear(); + if (selectAll) { + for (int i = 0, max = getRowCount(); i < max; i++) { + selected.add(i); + } + } + fireTableDataChanged(); + } +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.css b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.css new file mode 100644 index 0000000..e1c7770 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.css @@ -0,0 +1,41 @@ +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +#tabPanel { + layout:{new BorderLayout()}; + border:{new LineBorder(Color.BLACK,1,true)}; +} + +#progress { + indeterminate:false; + stringPainted:true; + string:{getProgressString(model.getStepIndex(model.getStep()), model.getSteps().size())}; + value:{1 + model.getStepIndex(model.getStep())}; + maximum:{model.getSteps().size()}; +} + +#descriptionPane { + columnHeaderView:{new JLabel(_("i18neditor.common.descrition"), jaxx.runtime.Util.getUIManagerActionIcon("information"), 10)}; +} + +#description { + rows:3; + editable:false; + focusable:false; +} diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.jaxx new file mode 100644 index 0000000..dbdd06f --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/AbstractProjectTabPanelUI.jaxx @@ -0,0 +1,88 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<JPanel id="tabPanel" + abstract='true' + implements='jaxx.runtime.swing.wizard.WizardStepUI<ProjectStep, ProjectUIModel>'> + + <style source='AbstractProjectTabPanelUI.css'/> + + <script><![CDATA[ +import static org.nuiton.i18n.I18n.n_; +import org.nuiton.i18n.editor.I18nEditorContext; +import org.nuiton.i18n.editor.I18nEditorConfig; +import org.nuiton.i18n.editor.ui.I18nEditorUIHandler; +import org.nuiton.i18n.editor.ui.project.ProjectStep; +import org.nuiton.i18n.editor.ui.project.ProjectUIModel; + +import jaxx.runtime.swing.wizard.WizardOperationState; + +protected I18nEditorUIHandler handler = getContextValue(I18nEditorUIHandler.class); +protected I18nEditorConfig config = getContextValue(I18nEditorConfig.class); +protected I18nEditorContext context = getContextValue(I18nEditorContext.class); +protected ProjectUIModel model = getContextValue(ProjectUIModel.class); + +@Override +public ProjectStep getStep() { + return (ProjectStep) getClientProperty("step"); +} + +public ProjectUIModel getModel() { + return model; +} + +protected void setDescriptionText(String text) { + description.setText(text); +} + +protected String getProgressString(int currentStep, int nbStep) { + ProjectStep step = getStep(); + String txt = ""; + if (step != null) { + txt = n_("i18neditor.project.step.label"); + txt = _(txt, currentStep + 1, nbStep, _(step.getLabel())); + } + return txt; +} +]]></script> + + <!-- layout pour la configuration specifique de chaque type de projet + on expose ici le layout sinon cela ne fonctionne pas (layout cree + apres 'content' : a fixer dans JAXX) --> + <jaxx.runtime.swing.CardLayout2Ext id='contentLayout' + constructorParams='this, "content"'/> + + <!-- titre --> + <JPanel layout='{new BorderLayout()}' constraints='BorderLayout.NORTH'> + <JProgressBar id='progress' constraints='BorderLayout.CENTER'/> + </JPanel> + + <!-- content --> + <JPanel id='content' constraints='BorderLayout.CENTER'/> + + <!-- description --> + <JScrollPane id="descriptionPane" constraints='BorderLayout.SOUTH'> + <JTextArea id='description'/> + </JScrollPane> + +</JPanel> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ChooseProjectTypePanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ChooseProjectTypePanelUI.jaxx new file mode 100644 index 0000000..9a011d5 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ChooseProjectTypePanelUI.jaxx @@ -0,0 +1,72 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> +<!-- ***************************************** --> +<!-- L'écran de sélection de type de connexion --> +<!-- ***************************************** --> +<AbstractProjectTabPanelUI _step='{ProjectStep.CHOOSE_PROJECT_TYPE}'> + + <script><![CDATA[ +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.*; +import org.nuiton.i18n.editor.ui.project.*; + +void $afterCompleteSetup() { + // ajout des types de projets connus a partir des providers connus + int index = 0; + for (I18nProjectProvider<?> p : I18nProjectFactory.getProviders()) { + JRadioButton b = new JRadioButton(); + String name = "$JRadioButton" + index; + $objectMap.put( name, b); + b.setName(name); + b.putClientProperty("$buttonGroup", mode); + mode.add(b); + b.setText(_(p.getLabel())); + b.setToolTipText(_(p.getDescription())); + b.putClientProperty("$value", p); + mode.updateSelectedValue(); + content.add(b, new GridBagConstraints(0, index++, 1, 1, 1.0, 0.0, 10, 1, new Insets(3, 3, 3, 3), 0, 0)); + } + // on ecoute les changement de type de projet + model.addPropertyChangeListener(ProjectUIModel.TYPE_PROPERTY_NAME, new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + I18nProjectProvider<?> newMode = (I18nProjectProvider<?>) evt.getNewValue(); + if (newMode != null) { + log.info("new project type : " + newMode); + AbstractButton c = mode.getButton(newMode); + c.setSelected(true); + setDescriptionText(_(newMode.getDescription())); + } + } + }); +} +]]> + </script> + + <ButtonGroup id='mode' + onStateChanged='model.setType((I18nProjectProvider) mode.getSelectedValue())'/> + + <Table id='content' fill='both' weightx='1' constraints='BorderLayout.CENTER'/> + +</AbstractProjectTabPanelUI> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ConfigureProjectPanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ConfigureProjectPanelUI.jaxx new file mode 100644 index 0000000..d83ca23 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ConfigureProjectPanelUI.jaxx @@ -0,0 +1,70 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<AbstractProjectTabPanelUI _step='{ProjectStep.CONFIGURE_PROJECT}'> + + <script><![CDATA[ + +import java.lang.reflect.Constructor; + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.I18nProjectProvider; +import org.nuiton.i18n.editor.project.I18nProjectFactory; +import org.nuiton.i18n.editor.project.I18nProjectConfigurePanelUI; +import org.nuiton.i18n.editor.ui.project.*; + +protected String updateContentLayout(I18nProjectProvider<?> projectType) { + if (projectType == null) { + return "null"; + } + return projectType.getType().getName(); +} + +void $afterCompleteSetup() { + if (getStep()!=null) { + setDescriptionText(_(getStep().getDescription())); + } + for (I18nProjectProvider<?> p : I18nProjectFactory.getProviders()) { + try { + Class<? extends I18nProjectConfigurePanelUI> c = p.getUIClass(); + Constructor<? extends I18nProjectConfigurePanelUI> constructor = c.getConstructor(jaxx.runtime.JAXXContext.class); + I18nProjectConfigurePanelUI ui = constructor.newInstance(this); + getContent().add((java.awt.Component) ui, p.getType().getName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} +]]> + </script> + + <jaxx.runtime.swing.CardLayout2Ext id='contentLayout' + selected='{updateContentLayout(model.getType())}'/> + + <JPanel id='content' layout='{contentLayout}'> + + <JLabel text='i18neditor.createproject.no.type' constraints='"null"'/> + + </JPanel> + +</AbstractProjectTabPanelUI> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/PersistPanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/PersistPanelUI.jaxx new file mode 100644 index 0000000..6b0d536 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/PersistPanelUI.jaxx @@ -0,0 +1,174 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<AbstractProjectTabPanelUI _step='{ProjectStep.PERSIST}'> + + <script><![CDATA[ +import jaxx.runtime.SwingUtil; + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.project.I18nProject; +import org.nuiton.i18n.editor.project.AbstractI18nProject; +import org.nuiton.i18n.editor.ui.project.*; + +import java.io.File; + +protected final I18nEditorConfig config = getContextValue(I18nEditorConfig.class); + +protected final PropertyChangeListener storeProjectPropertyChangeListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + Boolean oldValue = (Boolean) evt.getOldValue(); + Boolean newValue = (Boolean) evt.getNewValue(); + if (newValue != null && newValue) { + if (getProject().getStoreFile() == null || getProject().getStoreFile().getParentFile() == null) { + getProject().setStoreFile(new File(config.getProjectsDirectory(), model.getProject().getName() + ".i18nproject")); + } + } + } +}; + +public void chooseProjectDirectory() { + File f = model.chooseDirectory ( + this, + _("i18neditor.title.choose.projectDir"), + _("i18neditor.action.choose.projectDir"), + new File(directoryText.getText())); + changeDirectory(f); +} + +public I18nProject getProject() { + return model.getProject(); +} + +protected void changeDirectory(File f) { + getProject().setStoreFile(new File(f, getProject().getName() + ".i18nproject")); +} + +void $afterCompleteSetup() { + if (getStep()!=null) { + setDescriptionText(_(getStep().getDescription())); + } + model.addPropertyChangeListener(ProjectUIModel.PROJECT_PROPERTY_NAME, new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + I18nProject oldProject = (I18nProject) evt.getOldValue(); + if (oldProject!=null) { + oldProject.removePropertyChangeListener(AbstractI18nProject.STORE_PROJECT_PROPERTY_NAME, storeProjectPropertyChangeListener); + } + I18nProject newProject = (I18nProject) evt.getNewValue(); + if (newProject!=null) { + newProject.addPropertyChangeListener(AbstractI18nProject.STORE_PROJECT_PROPERTY_NAME, storeProjectPropertyChangeListener); + } + if (log.isDebugEnabled()) { + log.debug("update binding for changed project " + newProject); + } + SwingUtil.applyDataBinding(PersistPanelUI.this, + "storeProject.selected", + "storeFilePanel.visible", + "directoryText.text", + "storeFilePath.text"); + } + }); +} + +@Override +public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + for (PropertyChangeListener l : getPropertyChangeListeners(propertyName)) { + if (l == listener) { + // already registred listener + if (log.isDebugEnabled()) { + log.debug("already registred listener for property " + propertyName + " : " + listener); + } + return; + } + } + super.addPropertyChangeListener(propertyName, listener); +} + +protected String getStoreDirectory(File storeFile) { + if (storeFile == null || storeFile.getParentFile() == null) { + return ""; + } + return storeFile.getParentFile().getAbsolutePath(); +} +]]> + </script> + + + <JPanel id='content' constraints='BorderLayout.CENTER' layout='{new BorderLayout()}'> + <JPanel constraints='BorderLayout.NORTH' + layout='{new BorderLayout()}' + border='{BorderFactory.createTitledBorder("")}'> + <JCheckBox id='storeProject' + constraints='BorderLayout.CENTER' + text='i18neditor.createproject.doPersist' + selected='{getProject().isStoreProject()}' + onStateChanged='getProject().setStoreProject(((JCheckBox)event.getSource()).isSelected())'/> + </JPanel> + + <Table id='storeFilePanel' + constraints='BorderLayout.CENTER' + visible='{getProject().isStoreProject()}'> + <row> + <cell weightx='1' fill="both"> + <Table fill='both'> + <row> + <cell columns='2'> + <JLabel text='i18neditor.createproject.storeProject.directory'/> + </cell> + </row> + <row> + <cell weightx='1' fill="horizontal"> + <JTextField id='directoryText' + text='{getStoreDirectory(getProject().getStoreFile())}' + onKeyReleased='changeDirectory(new File(((JTextField)event.getSource()).getText()))'/> + <!--visible='{model.getProject().isStoreProject()}'--> + </cell> + <cell anchor="east"> + <JButton actionIcon='fileChooser' + onActionPerformed="chooseProjectDirectory()"/> + <!--visible='{model.getProject().isStoreProject()}'--> + </cell> + </row> + <row> + <cell columns='2'> + <JLabel text='i18neditor.createproject.storeProject.path'/> + <!--visible='{model.getProject().isStoreProject()}'/>--> + </cell> + </row> + <row> + <cell columns='2'> + <JLabel id='storeFilePath' + text='{getProject().getStoreFile() + ""}'/> + <!--visible='{model.getProject().isStoreProject()}'/>--> + </cell> + </row> + </Table> + </cell> + </row> + </Table> + </JPanel> +</AbstractProjectTabPanelUI> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ResumePanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ResumePanelUI.jaxx new file mode 100644 index 0000000..4492d5b --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/ResumePanelUI.jaxx @@ -0,0 +1,57 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<AbstractProjectTabPanelUI _step='{ProjectStep.RESUME}'> + + <script><![CDATA[ + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.ui.project.*; + +void $afterCompleteSetup() { + if (getStep()!=null) { + setDescriptionText(_(getStep().getDescription())); + } +} + +String computeReport(ProjectUIModel model, ProjectStep step) { + return "TODO"; +} +]]> + </script> + + <JPanel id='content' + constraints='BorderLayout.CENTER' + layout='{new BorderLayout()}'> + <JScrollPane columnHeaderView='{new JLabel(_("i18neditor.common.resume"))}' + constraints='BorderLayout.CENTER'> + <JTextArea id='resume' + text='{computeReport(model, model.getStep())}' + editable='false' + focusable='false' + rows='10' + /> + </JScrollPane> + </JPanel> + +</AbstractProjectTabPanelUI> diff --git a/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/SelectBundlesPanelUI.jaxx b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/SelectBundlesPanelUI.jaxx new file mode 100644 index 0000000..af3ae05 --- /dev/null +++ b/nuiton-i18n-editor/src/main/java/org/nuiton/i18n/editor/ui/project/tabs/SelectBundlesPanelUI.jaxx @@ -0,0 +1,57 @@ +<!-- + +/** + * *##% Nuiton I18n Editor + * Copyright (C) 2008 - 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. ##%* + */ + +--> + +<AbstractProjectTabPanelUI _step='{ProjectStep.SELECT_BUNDLES}'> + + <script><![CDATA[ +import jaxx.runtime.SwingUtil; + +import org.nuiton.i18n.editor.*; +import org.nuiton.i18n.editor.ui.project.*; + + +void $afterCompleteSetup() { + if (getStep() != null) { + setDescriptionText(_(getStep().getDescription())); + } +} +]]> + </script> + + <SelectBundlesTableModel id='bundlesModel' + onTableChanged='model.getProject().setUrls(bundlesModel.getSelectedUrls())'/> + + <Table id='content' constraints='BorderLayout.CENTER' fill='both' weightx='1' weighty='1'> + <row> + <cell> + <JScrollPane columnHeaderView="{bundles.getTableHeader()}" + verticalScrollBarPolicy="{JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED}"> + + <JTable id='bundles' + model='{bundlesModel}'/> + </JScrollPane> + </cell> + </row> + </Table> + +</AbstractProjectTabPanelUI> diff --git a/nuiton-i18n-editor/src/main/jnlp/jxlayer.jnlp b/nuiton-i18n-editor/src/main/jnlp/jxlayer.jnlp new file mode 100644 index 0000000..9f90ec8 --- /dev/null +++ b/nuiton-i18n-editor/src/main/jnlp/jxlayer.jnlp @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<jnlp spec="1.0+" codebase="@url@" href="jxlayer.jnlp"> + <information> + <title>Swing X</title> + <vendor>Swing X</vendor> + <offline-allowed/> + </information> + <resources> + <jar href="lib/@lib@"/> + </resources> + <component-desc/> +</jnlp> \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/jnlp/sun.jnlp b/nuiton-i18n-editor/src/main/jnlp/sun.jnlp new file mode 100644 index 0000000..f345d95 --- /dev/null +++ b/nuiton-i18n-editor/src/main/jnlp/sun.jnlp @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<jnlp spec="1.0+" codebase="@url@" href="sun.jnlp"> + <information> + <title>Sun MicroSystems</title> + <vendor>Sun MicroSystems, Inc.</vendor> + <offline-allowed/> + </information> + <resources> + <jar href="lib/@lib@"/> + </resources> + <component-desc/> +</jnlp> \ No newline at end of file diff --git a/nuiton-i18n-editor/src/main/resources/META-INF/services/org.nuiton.i18n.editor.project.I18nProjectProvider b/nuiton-i18n-editor/src/main/resources/META-INF/services/org.nuiton.i18n.editor.project.I18nProjectProvider new file mode 100644 index 0000000..11ba62d --- /dev/null +++ b/nuiton-i18n-editor/src/main/resources/META-INF/services/org.nuiton.i18n.editor.project.I18nProjectProvider @@ -0,0 +1,2 @@ +org.nuiton.i18n.editor.project.impl.JarI18nProjectProvider +org.nuiton.i18n.editor.project.impl.DirectoryI18nProjectProvider diff --git a/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-en_GB.properties b/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-en_GB.properties new file mode 100644 index 0000000..dbd0bbb --- /dev/null +++ b/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-en_GB.properties @@ -0,0 +1,120 @@ +i18neditor.about.message=<h3>Nuiton i18n editor</h3><hr/><p>Project realised by <a href\="http\://codelutin.com">Codelutin</a> in 2009.</p><br/><hr/>For more information, visit the <a href\="http\://maven-site.nuiton.org/i18n/i18neditor">project's site</a>. +i18neditor.action.about=About +i18neditor.action.about.tip=Show about screen +i18neditor.action.apply=Apply +i18neditor.action.apply.tip= +i18neditor.action.cancel=Cancel +i18neditor.action.cancel.tip=Cancel +i18neditor.action.choose.directory.source.description= +i18neditor.action.choose.jar.source= +i18neditor.action.choose.jar.source.description= +i18neditor.action.choose.project= +i18neditor.action.choose.project.description= +i18neditor.action.choose.projectDir= +i18neditor.action.closeProject=Close the project +i18neditor.action.commandline.configure=Configure the application +i18neditor.action.commandline.disable.main.ui=Do not launch main ui +i18neditor.action.commandline.help=Show this help +i18neditor.action.configuration=Preferences +i18neditor.action.configuration.tip=show preferences screen +i18neditor.action.create=Create +i18neditor.action.createProject=Create project +i18neditor.action.delete.key.tip= +i18neditor.action.deleteProject=Delete project +i18neditor.action.editProject=Modify project +i18neditor.action.exit=Exit +i18neditor.action.exit.tip=Quit +i18neditor.action.fullscreen=Full screen +i18neditor.action.fullscreen.tip=Go into full screen mode +i18neditor.action.goto.next.stage=Continue +i18neditor.action.goto.next.stage.tip= +i18neditor.action.goto.previous.stage=Go back +i18neditor.action.goto.previous.stage.tip= +i18neditor.action.help=Show help +i18neditor.action.help.tip=Show help +i18neditor.action.new.bundle=New +i18neditor.action.new.package=New +i18neditor.action.normalscreen=Frame mode +i18neditor.action.normalscreen.tip=Go back in frame mode +i18neditor.action.openOtherProject=Open other project +i18neditor.action.openProject=Open a project +i18neditor.action.reset.value.tip= +i18neditor.action.save.value.tip= +i18neditor.action.showHelp.tip=Show context sensitve help +i18neditor.action.site=Website +i18neditor.action.site.tip=Go to web site of application +i18neditor.bundle.name=Locale \: %1$s +i18neditor.choose.project.to.delete= +i18neditor.common.descrition= +i18neditor.common.resume=Resume +i18neditor.config.category.directories=Directories +i18neditor.config.category.directories.description=Directories used by the application +i18neditor.config.category.ui=Application +i18neditor.config.category.ui.description=Other options used by the application +i18neditor.config.configFileName.description=configuration file +i18neditor.config.defaultProjectsDirectory.description=default directory where to store project definitions (*.i18nproject) +i18neditor.config.defaultTmpDirectory.description=tempory directory used by the application +i18neditor.config.ui.fullscreen=choose initial ui mode (true for full screen, false otherwise) +i18neditor.config.ui.locale=Language used by application (fr_FR, en_GB) +i18neditor.createProject.table.bundles.select= +i18neditor.createProject.table.bundles.select.tip= +i18neditor.createProject.table.bundles.url= +i18neditor.createProject.table.bundles.url.tip= +i18neditor.createbundle.label=Create a new bundle for all packages known +i18neditor.createbundle.newlanguage=NEw language +i18neditor.createbundle.newlocale=New locale +i18neditor.createbundle.title=Create bundle +i18neditor.createpackage.label=Create a new package (for all bundles known) +i18neditor.createpackage.newpackage=NEw language +i18neditor.createpackage.title=Create package +i18neditor.createproject.common.select= +i18neditor.createproject.directory.source.label= +i18neditor.createproject.doPersist= +i18neditor.createproject.jar.source.label= +i18neditor.createproject.name.label= +i18neditor.createproject.no.type= +i18neditor.createproject.storeProject.directory= +i18neditor.createproject.storeProject.path= +i18neditor.createproject.uniqueJarDefinition= +i18neditor.createproject.uniqueJarDefinition.tip= +i18neditor.createproject.url= +i18neditor.init.closed=Application was closed at %1$s +i18neditor.init.context.done=Initialisation of context done in %1$s. +i18neditor.init.ui.done=GUI initialized +i18neditor.main.menu.file=File +i18neditor.menu.bundles=Bundles +i18neditor.menu.help=Help +i18neditor.menu.packages=Packages +i18neditor.menu.storeall=Save all +i18neditor.message.config.loaded=Config of nuiton-i18n-editor v %1$s was loaded. +i18neditor.message.help.usage=Help of nuiton-i18n-editor v %1$s +i18neditor.message.no.project.loaded=< No project loaded > +i18neditor.no.keys=< No defined key > +i18neditor.package.name=Package \: %1$s +i18neditor.project.info.tip=Click here to display project's informations +i18neditor.project.step.chooseProjectType= +i18neditor.project.step.chooseProjectType.description= +i18neditor.project.step.configureProject= +i18neditor.project.step.configureProject.description= +i18neditor.project.step.label= +i18neditor.project.step.persist= +i18neditor.project.step.persist.description= +i18neditor.project.step.resume= +i18neditor.project.step.resume.description= +i18neditor.project.step.selecteBundles= +i18neditor.project.step.selecteBundles.description= +i18neditor.project.type.directory= +i18neditor.project.type.directory.description= +i18neditor.project.type.jar= +i18neditor.project.type.jar.description= +i18neditor.resource.name=Resource \: %1$s +i18neditor.title=Nuiton i18n editor < Project %1$s > +i18neditor.title.about=About Nuiton i18n editor +i18neditor.title.choose.directory.source= +i18neditor.title.choose.jar.source= +i18neditor.title.choose.project= +i18neditor.title.choose.projectDir= +i18neditor.title.noproject=Nuiton i18n editor < no project loaded > +i18neditor.title.project.info=Project's informations +i18neditor.warning.nimbus.landf=Could not find nymbus look and feel, please use at least a 1.6u10 version of sun jre +i18neditor.warning.no.ui=No ui environment detected\! diff --git a/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-fr_FR.properties b/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-fr_FR.properties new file mode 100644 index 0000000..78ed8ee --- /dev/null +++ b/nuiton-i18n-editor/src/main/resources/i18n/nuiton-i18n-editor-fr_FR.properties @@ -0,0 +1,120 @@ +i18neditor.about.message=<h3>Nuiton i18n editor</h3><hr/><p>Projet r\u00E9alis\u00E9 par la soci\u00E9t\u00E9 <a href\="http\://codelutin.com">Codelutin</a> en 2009.</p><br/><hr/>Pour plus d'informations, vous pouvez visiter le <a href\="http\://maven-site.nuiton.org/i18n/i18neditor">site du projet</a>. +i18neditor.action.about=A propos +i18neditor.action.about.tip=Afficher l'a propos +i18neditor.action.apply=Appliquer +i18neditor.action.apply.tip=Appliquer +i18neditor.action.cancel=Annuler +i18neditor.action.cancel.tip=Annuler +i18neditor.action.choose.directory.source.description=R\u00E9pertoire +i18neditor.action.choose.jar.source=Choisir ce jar +i18neditor.action.choose.jar.source.description=Fichier jar (*.jar) +i18neditor.action.choose.project=Choisir ce projet +i18neditor.action.choose.project.description=Fichier de d\u00E9finition de project (*.i18nproject) +i18neditor.action.choose.projectDir=Choisir ce r\u00E9pertoire +i18neditor.action.closeProject=Fermer le projet +i18neditor.action.commandline.configure=Configurer l'application +i18neditor.action.commandline.disable.main.ui=Ne pas lancer l'interface graphique principale +i18neditor.action.commandline.help=Voir l'aide +i18neditor.action.configuration=Pr\u00E9f\u00E9rences +i18neditor.action.configuration.tip=Modifier les pr\u00E9f\u00E9rences +i18neditor.action.create=Cr\u00E9er +i18neditor.action.createProject=Cr\u00E9er un projet +i18neditor.action.delete.key.tip=Supprimer cette traduction +i18neditor.action.deleteProject=Supprimer un projet +i18neditor.action.editProject=Modifier le projet +i18neditor.action.exit=Quitter +i18neditor.action.exit.tip=Quitter l'application +i18neditor.action.fullscreen=Plein \u00E9cran +i18neditor.action.fullscreen.tip=Passer en mode Plein \u00E9cran +i18neditor.action.goto.next.stage=Continuer +i18neditor.action.goto.next.stage.tip=Continuer vers l'\u00E9tape suivante +i18neditor.action.goto.previous.stage=Revenir +i18neditor.action.goto.previous.stage.tip=Revenir \u00E0 l'\u00E9tape pr\u00E9c\u00E9dente +i18neditor.action.help=Aide +i18neditor.action.help.tip=Affichier l'aide +i18neditor.action.new.bundle=Nouveau +i18neditor.action.new.package=Nouveau +i18neditor.action.normalscreen=Mode fen\u00EAtre +i18neditor.action.normalscreen.tip=retour en mode fen\u00EAtre +i18neditor.action.openOtherProject=Ouvrir un projet (autre r\u00E9pertoire) +i18neditor.action.openProject=Ouvrir un projet... +i18neditor.action.reset.value.tip=R\u00E9initialiser +i18neditor.action.save.value.tip=Sauver la traduction modifi\u00E9e +i18neditor.action.showHelp.tip=Voir l'aide contextuelle +i18neditor.action.site=Site +i18neditor.action.site.tip=Acc\u00E9der au site du projet +i18neditor.bundle.name=Localisation \: %1$s +i18neditor.choose.project.to.delete=Choisir le projet \u00E0 supprimer +i18neditor.common.descrition=Description +i18neditor.common.resume=R\u00E9sum\u00E9 +i18neditor.config.category.directories=R\u00E9pertoires +i18neditor.config.category.directories.description=Les r\u00E9pertoires utilis\u00E9es par l'application +i18neditor.config.category.ui=Application +i18neditor.config.category.ui.description=Les autres options de l'application +i18neditor.config.configFileName.description=Le nom du fichier de configuration +i18neditor.config.defaultProjectsDirectory.description=Le r\u00E9pertoire o\u00F9 sont sauvegarder les d\u00E9finitions de projets +i18neditor.config.defaultTmpDirectory.description=Le r\u00E9pertoire temporaire par d\u00E9faut +i18neditor.config.ui.fullscreen=Pour afficher l'aplication en mode pleine \u00E9cran +i18neditor.config.ui.locale=Langue utilis\u00E9e par l'application (fr_FR, en_GB) +i18neditor.createProject.table.bundles.select=... +i18neditor.createProject.table.bundles.select.tip=S\u00E9lectionner - D\u00E9selectionner toutes les urls +i18neditor.createProject.table.bundles.url=Localisation +i18neditor.createProject.table.bundles.url.tip=location du bundle +i18neditor.createbundle.label=Cr\u00E9er un nouveau bundle pour les paquetages donn\u00E9es +i18neditor.createbundle.newlanguage=Langue +i18neditor.createbundle.newlocale=Pays +i18neditor.createbundle.title=Nouveau bundle +i18neditor.createpackage.label=Cr\u00E9er un nouveau paquetage (pour tous les bundles connus) +i18neditor.createpackage.newpackage=Nouveau paquetage +i18neditor.createpackage.title=Cr\u00E9er un nouveau paquetage +i18neditor.createproject.common.select=... +i18neditor.createproject.directory.source.label=R\u00E9pertoire de sauvegarde +i18neditor.createproject.doPersist=Sauver la d\u00E9finition du projet +i18neditor.createproject.jar.source.label=Choisir le jar source +i18neditor.createproject.name.label=Nom du projet +i18neditor.createproject.no.type=< Aucune type de projet s\u00E9lectionn\u00E9 > +i18neditor.createproject.storeProject.directory=R\u00E9pertoire de sauvegarde +i18neditor.createproject.storeProject.path=Fichier de sauvegarde +i18neditor.createproject.uniqueJarDefinition=Utilisation d'un bundle final +i18neditor.createproject.uniqueJarDefinition.tip=Utilisation d'un bundle final (un seul bundle) +i18neditor.createproject.url=Localisation +i18neditor.init.closed=Nuiton-i18n-editor a \u00E9t\u00E9 ferm\u00E9 \u00E0 %1$s +i18neditor.init.context.done=Initialisation du contexte termin\u00E9e en %1$s. +i18neditor.init.ui.done=Initialisation de l'interface graphique termin\u00E9e. +i18neditor.main.menu.file=Fichier +i18neditor.menu.bundles=Bundles +i18neditor.menu.help=Aide +i18neditor.menu.packages=Paquetages +i18neditor.menu.storeall=Sauvegarder +i18neditor.message.config.loaded=Configuration de nuiton-i18n-editor v. %1$s charg\u00E9e. +i18neditor.message.help.usage=Aide de nuiton-i18n-editor v %1$s +i18neditor.message.no.project.loaded=< Aucun projet charg\u00E9 > +i18neditor.no.keys=< Aucune clef definie > +i18neditor.package.name=Paquetage \: %1$s +i18neditor.project.info.tip=Cliquer ici pour obtenir des informations sur le projet charg\u00E9 +i18neditor.project.step.chooseProjectType=Type de projet +i18neditor.project.step.chooseProjectType.description=Choisir le type de projet \u00E0 ouvrir +i18neditor.project.step.configureProject=Configuration +i18neditor.project.step.configureProject.description=Configurer le projet +i18neditor.project.step.label=Etape %1$d/%2$d \: %3$s +i18neditor.project.step.persist=Sauvegarde +i18neditor.project.step.persist.description=Permet de sauvegarder la d\u00E9finition du projet +i18neditor.project.step.resume=Resume +i18neditor.project.step.resume.description=Voir le r\u00E9sum\u00E9 du projet \u00E0 ouvrir +i18neditor.project.step.selecteBundles=Bundles +i18neditor.project.step.selecteBundles.description=Choisir les bundles du projet parmi ceux d\u00E9tect\u00E9s +i18neditor.project.type.directory=R\u00E9pertoire +i18neditor.project.type.directory.description=Ouvrir un projet \u00E0 partir d'un r\u00E9pertoire +i18neditor.project.type.jar=Jar (ou zip) +i18neditor.project.type.jar.description=Ouvrir un projet \u00E0 partir d'un jar +i18neditor.resource.name=Resource \: %1$s +i18neditor.title=Nuiton i18n editor < Projet %1$s > +i18neditor.title.about=A propos de nuiton-i18n-editor... +i18neditor.title.choose.directory.source=Choisir le r\u00E9pertoire +i18neditor.title.choose.jar.source=Choisir un fichier de type 'jar' +i18neditor.title.choose.project=Choisir le fichier de d\u00E9finition d'un projet +i18neditor.title.choose.projectDir=Choisir une d\u00E9finition de projet +i18neditor.title.noproject=Nuiton i18n editor < aucun projet charg\u00E9 > +i18neditor.title.project.info=Informations sur le projet +i18neditor.warning.nimbus.landf=Le look and Feel Nimbus n'a pas \u00E9t\u00E9 trouv\u00E9, il faut au moins la version 1.6u10 de java. +i18neditor.warning.no.ui=Aucun environnement graphique d\u00E9tect\u00E9 diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-about.png b/nuiton-i18n-editor/src/main/resources/icons/action-about.png new file mode 100644 index 0000000..0d826bb Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-about.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-accept.png b/nuiton-i18n-editor/src/main/resources/icons/action-accept.png new file mode 100644 index 0000000..89c8129 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-accept.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-add.png b/nuiton-i18n-editor/src/main/resources/icons/action-add.png new file mode 100644 index 0000000..6332fef Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-add.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-calculator.png b/nuiton-i18n-editor/src/main/resources/icons/action-calculator.png new file mode 100644 index 0000000..701a60a Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-calculator.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-cancel.png b/nuiton-i18n-editor/src/main/resources/icons/action-cancel.png new file mode 100644 index 0000000..c149c2b Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-cancel.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-close.png b/nuiton-i18n-editor/src/main/resources/icons/action-close.png new file mode 100644 index 0000000..2541d2b Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-close.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-closeTab.png b/nuiton-i18n-editor/src/main/resources/icons/action-closeTab.png new file mode 100644 index 0000000..93edaf0 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-closeTab.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-collapseAll.png b/nuiton-i18n-editor/src/main/resources/icons/action-collapseAll.png new file mode 100644 index 0000000..f88a24a Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-collapseAll.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-combobox-reset.png b/nuiton-i18n-editor/src/main/resources/icons/action-combobox-reset.png new file mode 100644 index 0000000..0fb00f9 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-combobox-reset.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-combobox-sort.png b/nuiton-i18n-editor/src/main/resources/icons/action-combobox-sort.png new file mode 100644 index 0000000..188e1c1 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-combobox-sort.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-config.png b/nuiton-i18n-editor/src/main/resources/icons/action-config.png new file mode 100644 index 0000000..9460dfc Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-config.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-connect_creating.png b/nuiton-i18n-editor/src/main/resources/icons/action-connect_creating.png new file mode 100644 index 0000000..258b65b Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-connect_creating.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-connect_no.png b/nuiton-i18n-editor/src/main/resources/icons/action-connect_no.png new file mode 100644 index 0000000..3c832d4 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-connect_no.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-connect_ok.png b/nuiton-i18n-editor/src/main/resources/icons/action-connect_ok.png new file mode 100644 index 0000000..2bd16cc Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-connect_ok.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-connect_untested.png b/nuiton-i18n-editor/src/main/resources/icons/action-connect_untested.png new file mode 100644 index 0000000..61a8556 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-connect_untested.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-connected.png b/nuiton-i18n-editor/src/main/resources/icons/action-connected.png new file mode 100644 index 0000000..024138e Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-connected.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-db-change.png b/nuiton-i18n-editor/src/main/resources/icons/action-db-change.png new file mode 100644 index 0000000..3a11197 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-db-change.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-db-local.png b/nuiton-i18n-editor/src/main/resources/icons/action-db-local.png new file mode 100644 index 0000000..fed6221 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-db-local.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-db-none.png b/nuiton-i18n-editor/src/main/resources/icons/action-db-none.png new file mode 100644 index 0000000..cce652e Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-db-none.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-db-remote.png b/nuiton-i18n-editor/src/main/resources/icons/action-db-remote.png new file mode 100644 index 0000000..49b2691 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-db-remote.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-delete.png b/nuiton-i18n-editor/src/main/resources/icons/action-delete.png new file mode 100644 index 0000000..184f762 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-delete.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-edit.png b/nuiton-i18n-editor/src/main/resources/icons/action-edit.png new file mode 100644 index 0000000..188e1c1 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-edit.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-exit.png b/nuiton-i18n-editor/src/main/resources/icons/action-exit.png new file mode 100644 index 0000000..a77152b Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-exit.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-expandAll.png b/nuiton-i18n-editor/src/main/resources/icons/action-expandAll.png new file mode 100644 index 0000000..3c19e48 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-expandAll.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-fileChooser.png b/nuiton-i18n-editor/src/main/resources/icons/action-fileChooser.png new file mode 100644 index 0000000..523a651 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-fileChooser.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-fullscreen.png b/nuiton-i18n-editor/src/main/resources/icons/action-fullscreen.png new file mode 100644 index 0000000..6845e04 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-fullscreen.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-go-back.png b/nuiton-i18n-editor/src/main/resources/icons/action-go-back.png new file mode 100644 index 0000000..5dc6967 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-go-back.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-go-detail.png b/nuiton-i18n-editor/src/main/resources/icons/action-go-detail.png new file mode 100644 index 0000000..aba044b Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-go-detail.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-go-down.png b/nuiton-i18n-editor/src/main/resources/icons/action-go-down.png new file mode 100644 index 0000000..2c4e279 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-go-down.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-go-jump.png b/nuiton-i18n-editor/src/main/resources/icons/action-go-jump.png new file mode 100644 index 0000000..1d218c3 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-go-jump.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-go-up.png b/nuiton-i18n-editor/src/main/resources/icons/action-go-up.png new file mode 100644 index 0000000..1ebb193 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-go-up.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-help.png b/nuiton-i18n-editor/src/main/resources/icons/action-help.png new file mode 100644 index 0000000..4ed65a9 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-help.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-i18n-es.png b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-es.png new file mode 100644 index 0000000..c2de2d7 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-es.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-i18n-fr.png b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-fr.png new file mode 100644 index 0000000..8332c4e Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-fr.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-i18n-gb.png b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-gb.png new file mode 100644 index 0000000..ff701e1 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-i18n-gb.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-import-gps.png b/nuiton-i18n-editor/src/main/resources/icons/action-import-gps.png new file mode 100644 index 0000000..68f21d3 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-import-gps.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-information.png b/nuiton-i18n-editor/src/main/resources/icons/action-information.png new file mode 100644 index 0000000..12cd1ae Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-information.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-leave-fullscreen.png b/nuiton-i18n-editor/src/main/resources/icons/action-leave-fullscreen.png new file mode 100644 index 0000000..fc6638e Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-leave-fullscreen.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-local-export.png b/nuiton-i18n-editor/src/main/resources/icons/action-local-export.png new file mode 100644 index 0000000..91e3b24 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-local-export.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-local-import.png b/nuiton-i18n-editor/src/main/resources/icons/action-local-import.png new file mode 100644 index 0000000..cafac61 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-local-import.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-mode-create.png b/nuiton-i18n-editor/src/main/resources/icons/action-mode-create.png new file mode 100644 index 0000000..e2f0847 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-mode-create.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-mode-read.png b/nuiton-i18n-editor/src/main/resources/icons/action-mode-read.png new file mode 100644 index 0000000..d8e23ec Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-mode-read.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-mode-update.png b/nuiton-i18n-editor/src/main/resources/icons/action-mode-update.png new file mode 100644 index 0000000..6e756cc Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-mode-update.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-next-step.png b/nuiton-i18n-editor/src/main/resources/icons/action-next-step.png new file mode 100644 index 0000000..6ef8de7 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-next-step.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-open.png b/nuiton-i18n-editor/src/main/resources/icons/action-open.png new file mode 100644 index 0000000..41676a0 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-open.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-previous-step.png b/nuiton-i18n-editor/src/main/resources/icons/action-previous-step.png new file mode 100644 index 0000000..659cd90 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-previous-step.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-remote-export.png b/nuiton-i18n-editor/src/main/resources/icons/action-remote-export.png new file mode 100644 index 0000000..44c06dd Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-remote-export.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-remote-import.png b/nuiton-i18n-editor/src/main/resources/icons/action-remote-import.png new file mode 100644 index 0000000..ff803be Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-remote-import.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-revert.png b/nuiton-i18n-editor/src/main/resources/icons/action-revert.png new file mode 100644 index 0000000..197b8a7 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-revert.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-save.png b/nuiton-i18n-editor/src/main/resources/icons/action-save.png new file mode 100644 index 0000000..41b3f43 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-save.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-select-ssl-cert.png b/nuiton-i18n-editor/src/main/resources/icons/action-select-ssl-cert.png new file mode 100644 index 0000000..ca93f0d Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-select-ssl-cert.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-show-help.png b/nuiton-i18n-editor/src/main/resources/icons/action-show-help.png new file mode 100644 index 0000000..f6bc721 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-show-help.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-site.png b/nuiton-i18n-editor/src/main/resources/icons/action-site.png new file mode 100644 index 0000000..ac5957a Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-site.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-synch.png b/nuiton-i18n-editor/src/main/resources/icons/action-synch.png new file mode 100644 index 0000000..3fd71d6 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-synch.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-synchro-pause.png b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-pause.png new file mode 100644 index 0000000..2d9ce9c Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-pause.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-synchro-start.png b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-start.png new file mode 100644 index 0000000..0846555 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-start.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-synchro-stop.png b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-stop.png new file mode 100644 index 0000000..893bb60 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-synchro-stop.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-translate.png b/nuiton-i18n-editor/src/main/resources/icons/action-translate.png new file mode 100644 index 0000000..63ce64d Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-translate.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-unconnected.png b/nuiton-i18n-editor/src/main/resources/icons/action-unconnected.png new file mode 100644 index 0000000..b335cb1 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-unconnected.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/action-validate.png b/nuiton-i18n-editor/src/main/resources/icons/action-validate.png new file mode 100644 index 0000000..a9925a0 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/action-validate.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/logo OT_rvb.png b/nuiton-i18n-editor/src/main/resources/icons/logo OT_rvb.png new file mode 100644 index 0000000..1c35a25 Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/logo OT_rvb.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/logo-OT_web.png b/nuiton-i18n-editor/src/main/resources/icons/logo-OT_web.png new file mode 100644 index 0000000..e6b87bd Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/logo-OT_web.png differ diff --git a/nuiton-i18n-editor/src/main/resources/icons/logo_ird.png b/nuiton-i18n-editor/src/main/resources/icons/logo_ird.png new file mode 100644 index 0000000..87405cc Binary files /dev/null and b/nuiton-i18n-editor/src/main/resources/icons/logo_ird.png differ diff --git a/nuiton-i18n-editor/src/main/resources/log4j.properties b/nuiton-i18n-editor/src/main/resources/log4j.properties new file mode 100644 index 0000000..ee9f182 --- /dev/null +++ b/nuiton-i18n-editor/src/main/resources/log4j.properties @@ -0,0 +1,14 @@ +# Global logging configuration +log4j.rootLogger=ERROR, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n +#log4j.appender.stdout.layout.ConversionPattern=%%c=%c %%C=%C %%d=%d %%F=%F %%l=%l %%L=%L %%m=%m %%M=%M %%p=%p %%r=%r %%t=%t %%x=%x %%X=%X +# package level +log4j.logger.org.nuiton=WARN +log4j.logger.org.nuiton.i18n.editor=INFO +#log4j.logger.org.nuiton.util.Resource=DEBUG +#log4j.logger.org.nuiton.i18n.editor.I18nEditorConfig=DEBUG +log4j.logger.jaxx.runtime.swing.editor.config=DEBUG +log4j.logger.jaxx.runtime.swing.editor=DEBUG diff --git a/nuiton-i18n-editor/src/site/apt/index.apt b/nuiton-i18n-editor/src/site/apt/index.apt new file mode 100644 index 0000000..fc25dc0 --- /dev/null +++ b/nuiton-i18n-editor/src/site/apt/index.apt @@ -0,0 +1,25 @@ +---- +Nuiton-i18n-editor +---- +---- +2009-08-22 +---- + +Présentation + + Application swing pour éditer des bundles i18n. + +Set it in action from Web + +~~[images/webstart.gif] webstart + + To run this application in + {{{http://java.sun.com/products/javawebstart/} Java Web Start}}, + click the {{{launch-nuiton-i18n-editor.jnlp}following link}}. + + Actually does not work very well, prefer use the zip version... + +Set it in action from Zip + + You can download at the section download the zip file, extract it and then + launch <go.sh> or <go.bat>. diff --git a/nuiton-i18n-editor/src/site/site.xml b/nuiton-i18n-editor/src/site/site.xml new file mode 100644 index 0000000..65b8a30 --- /dev/null +++ b/nuiton-i18n-editor/src/site/site.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="${project.name}"> + + <skin> + <groupId>org.nuiton</groupId> + <artifactId>maven-nuiton-skin</artifactId> + <version>1.0.0</version> + </skin> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <bannerRight> + <src>http://www.codelutin.com/images/lutinorange-codelutin.png</src> + <href>${project.organization.url}</href> + </bannerRight> + + <poweredBy> + <logo href="http://maven-site.nuiton.org/jaxx" name="JAXX" img="images/jaxx-logo.png"/> + <logo href="http://jrst.labs.libre-entreprise.org/jrst" name="JRst" + img="images/jrst-logo.png"/> + + <logo href="http://docutils.sourceforge.net/rst.html" name="ReStructuredText" + img="images/restructuredtext-logo.png"/> + </poweredBy> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="${project.url}" /> + </breadcrumbs> + + <menu name="Utilisateur"> + <item name="Accueil" href="index.html"/> + <item href="launch-${project.artifactId}.jnlp" + src="images/webstart.gif" + name="Démarrer la dernière version"/> + <item href="${project.build.finalName}-bin.zip" + name="Télécharger la dernière version"/> + </menu> + + <menu name="Développeur"> + <item name="A faire" href="Todo.html"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0d74e8e --- /dev/null +++ b/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + <parent> + <groupId>org.nuiton</groupId> + <artifactId>mavenpom</artifactId> + <version>1.0.0</version> + </parent> + + <artifactId>i18n</artifactId> + <version>1.0.0</version> + + <modules> + <module>nuiton-i18n-api</module> + <module>maven-i18n-plugin</module> + <!--module>nuiton-i18n-editor</module--> + </modules> + + <dependencyManagement> + <dependencies> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.1.1</version> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>1.8.0</version> + </dependency> + + <dependency> + <groupId>org.nuiton.processor</groupId> + <artifactId>nuiton-processor</artifactId> + <version>${processor.version}</version> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>maven-helper-plugin</artifactId> + <version>${helper.version}</version> + <type>maven-plugin</type> + </dependency> + + <dependency> + <groupId>xalan</groupId> + <artifactId>xalan</artifactId> + <version>2.7.1</version> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + <version>3.1</version> + </dependency> + + <!-- tests dependencies --> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.6</version> + <scope>test</scope> + </dependency> + + <!--dependency> + <groupId>org.nuiton</groupId> + <artifactId>maven-helper-plugin</artifactId> + <version>${helper.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency--> + + <!-- provided dependencies --> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>${maven.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + <version>${maven.version}</version> + <scope>provided</scope> + </dependency> + + <!-- FIXME si on ne le rajoute pas, on se retrouve avec la version 1.1 qui ne convient pas --> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + <version>1.5.15</version> + <scope>compile</scope> + </dependency> + + </dependencies> + </dependencyManagement> + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + <name>nuiton-i18n - pom</name> + <description>Nuiton i18n tools</description> + <inceptionYear>2008</inceptionYear> + <url>http://maven-site.nuiton.org/i18n</url> + + <!-- ************************************************************* --> + <!-- *** Build Settings ****************************************** --> + <!-- ************************************************************* --> + + <packaging>pom</packaging> + + <properties> + <!-- pour un muli module on doit fixer le projectId --> + <projectId>i18n</projectId> + + <processor.version>1.0.0</processor.version> + + </properties> + + <build> + + <defaultGoal>install</defaultGoal> + + </build> + + <!-- ************************************************************* --> + <!-- *** Build Environment ************************************** --> + <!-- ************************************************************* --> + + <!-- Source control management. --> + <scm> + <connection>scm:svn:http://svn.nuiton.org/svn/i18n/tags/i18n-1.0.0</connection> + <developerConnection>scm:svn:http://svn.nuiton.org/svn/i18n/tags/i18n-1.0.0</developerConnection> + <url>http://www.nuiton.org/repositories/browse/i18n/tags/i18n-1.0.0</url> + </scm> + +</project> + diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 0000000..644dfdc --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="${project.name}"> + + <publishDate format="dd/MM/yyyy"/> + + <skin> + <groupId>org.nuiton</groupId> + <artifactId>maven-nuiton-skin</artifactId> + <version>1.0.0</version> + </skin> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <bannerRight> + <src>http://www.codelutin.com/images/lutinorange-codelutin.png</src> + <href>${project.organization.url}</href> + </bannerRight> + + <poweredBy> + <logo href="http://maven.apache.org" name="Maven" img="${project.url}/images/logos/maven-feather.png"/> + </poweredBy> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="${project.url}" /> + </breadcrumbs> + + <menu ref="modules"/> + + <menu ref="reports"/> + </body> +</project> -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.