diff --git a/.gitattributes b/.gitattributes index 13edf646ed..72cdd367e3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,7 +5,7 @@ # converted to the OS's native line endings. * text -# Explicitly declare text files we want to always be normalized and converted +# Explicitly declare text files we want to always be normalized and converted # to native line endings on checkout. *.c text *.css text @@ -29,6 +29,10 @@ # Declare files that will always have CRLF line endings on checkout. *.sln text eol=crlf +# Declare files that will always have LF line endings on checkout. +build text eol=lf +mvnw text eol=lf + # Denote all files that are truly binary and should not be modified. *.gif binary *.jar binary diff --git a/LICENSE.LESSER.txt b/LICENSE.LESSER.txt deleted file mode 100644 index 551cb4acb9..0000000000 --- a/LICENSE.LESSER.txt +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. 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 not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -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 -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the 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 -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library 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 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d159169d10..0000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) 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 -this service 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 make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. 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. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -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 -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the 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 a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE 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. - - 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 -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 2 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, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision 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, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This 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. diff --git a/api/zanata-common-api/src/main/java/org/zanata/common/EntityStatus.java b/api/zanata-common-api/src/main/java/org/zanata/common/EntityStatus.java index 62c1952196..77ba53e64d 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/common/EntityStatus.java +++ b/api/zanata-common-api/src/main/java/org/zanata/common/EntityStatus.java @@ -20,13 +20,24 @@ */ package org.zanata.common; +import com.webcohesion.enunciate.metadata.Label; + import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlType; +/** + * The possible state of various entities in the system. + */ @XmlType(name = "entityStatusType") @XmlEnum(String.class) +@Label("Status") public enum EntityStatus { - ACTIVE("jsf.Active"), READONLY("jsf.ReadOnly"), OBSOLETE("jsf.Obsolete"); + /** Regular state for most entities. Means it is readable and writeable */ + ACTIVE("jsf.Active"), + /** Same as active, but cannot be modified */ + READONLY("jsf.ReadOnly"), + /** Removed from the system. An object in this state cannot be accessed. */ + OBSOLETE("jsf.Obsolete"); public static EntityStatus valueOf(char initial) { switch (initial) { diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/CopyTransStatus.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/CopyTransStatus.java index 6380218b77..3b4a46e2e4 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/CopyTransStatus.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/CopyTransStatus.java @@ -20,6 +20,9 @@ */ package org.zanata.rest.dto; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @@ -32,12 +35,17 @@ */ @XmlRootElement(name = "copyTransStatus") @XmlType(name = "copyTransStatusType") +@Label("Copy Trans Status") public class CopyTransStatus { private int percentageComplete; private boolean inProgress; + /** + * An estimated percentage of completion for the copy trans run. + */ @XmlElement(required = true) + @DocumentationExample("80") public int getPercentageComplete() { return percentageComplete; } @@ -46,6 +54,9 @@ public void setPercentageComplete(int percentageComplete) { this.percentageComplete = percentageComplete; } + /** + * True if the process is still running. False otherwise + */ @XmlElement(required = true) public boolean isInProgress() { return inProgress; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryEntry.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryEntry.java index 9801098371..230cdcc2d3 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryEntry.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryEntry.java @@ -30,6 +30,8 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -39,7 +41,8 @@ import org.zanata.rest.MediaTypes; /** - * + * A single glossary entry representing a single translated term in multiple + * locales. * @author Alex Eng aeng@redhat.com * **/ @@ -48,6 +51,7 @@ "description", "sourceReference", "glossaryTerms", "termsCount", "qualifiedName" }) @JsonPropertyOrder({ "id", "pos", "description", "srcLang", "sourceReference", "glossaryTerms", "termsCount", "qualifiedName" }) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Glossary Entry") public class GlossaryEntry implements Serializable, HasMediaType { /** * @@ -78,8 +82,12 @@ public GlossaryEntry(Long id) { this.id = id; } + /** + * Unique identifier + */ @XmlElement(name = "id", namespace = Namespaces.ZANATA_OLD) @JsonProperty("id") + @DocumentationExample(value = "444555", value2 = "444556") public Long getId() { return id; } @@ -88,8 +96,12 @@ public void setId(Long id) { this.id = id; } + /** + * Glossary entry's part of speech + */ @XmlElement(name = "pos", namespace = Namespaces.ZANATA_OLD) @JsonProperty("pos") + @DocumentationExample(value = "verb", value2 = "noun") public String getPos() { return pos; } @@ -108,8 +120,13 @@ public void setDescription(String description) { this.description = description; } + /** + * Number of translated terms. A term is the glossary entry's representation + * for a specific locale + */ @XmlElement(name = "termsCount", namespace = Namespaces.ZANATA_API) @JsonProperty("termsCount") + @DocumentationExample("2") public int getTermsCount() { return termsCount; } @@ -118,6 +135,9 @@ public void setTermsCount(int termsCount) { this.termsCount = termsCount; } + /** + * The full list of glossary terms + */ @XmlElement(name = "glossary-term", namespace = Namespaces.ZANATA_OLD) @JsonProperty("glossaryTerms") public List getGlossaryTerms() { @@ -131,9 +151,13 @@ public void setGlossaryTerms(List glossaryTerms) { this.glossaryTerms = glossaryTerms; } + /** + * The source locale for this specific entry + */ @XmlAttribute(name = "src-lang") @XmlJavaTypeAdapter(type = LocaleId.class, value = LocaleIdAdapter.class) @JsonProperty("srcLang") + @DocumentationExample("en-US") public LocaleId getSrcLang() { return srcLang; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryInfo.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryInfo.java index a100494b4b..30721f72b3 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryInfo.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryInfo.java @@ -7,12 +7,14 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.Namespaces; /** + * Information about a specific Glossary. * @author Alex Eng aeng@redhat.com */ @XmlRootElement(name = "glossaryInfo") @@ -20,6 +22,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonPropertyOrder({ "srcLocale", "transLocale"}) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Glossary Info") public class GlossaryInfo implements Serializable { private static final long serialVersionUID = -5688873815049369490L; private GlossaryLocaleInfo srcLocale; @@ -35,6 +38,9 @@ public GlossaryInfo(GlossaryLocaleInfo srcLocale, this.transLocale = transLocale; } + /** + * The glossary's source locale + */ @XmlElement(name = "srcLocale", required = false, namespace = Namespaces.ZANATA_API) public GlossaryLocaleInfo getSrcLocale() { @@ -45,6 +51,9 @@ public void setSrcLocale(GlossaryLocaleInfo srcLocale) { this.srcLocale = srcLocale; } + /** + * The list of translated locale's available for the glossary + */ @XmlElement(name = "transLocale", required = false, namespace = Namespaces.ZANATA_API) public List getTransLocale() { diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryLocaleInfo.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryLocaleInfo.java index 9955495933..458a932b19 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryLocaleInfo.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryLocaleInfo.java @@ -5,6 +5,8 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -18,6 +20,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonPropertyOrder({ "locale", "numberOfTerms"}) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Glossary Locale Info") public class GlossaryLocaleInfo implements Serializable { private static final long serialVersionUID = 7486128063191358182L; private LocaleDetails locale; @@ -42,8 +45,12 @@ public void setLocale(LocaleDetails locale) { this.locale = locale; } + /** + * Number of terms available for the glossary in this locale + */ @XmlElement(name = "numberOfTerms", required = false, namespace = Namespaces.ZANATA_API) + @DocumentationExample("2") public int getNumberOfTerms() { return numberOfTerms; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryResults.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryResults.java index f67a021a3e..8aa66745cb 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryResults.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryResults.java @@ -10,6 +10,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -17,8 +18,8 @@ import org.zanata.common.Namespaces; /** - * Wrapper for list of HGlossaryEntry/GlossaryEntry and list of warning message after - * saving/update + * Wrapper for list of Glossary entries and a list of warning messages after + * saving/updating * * @author Alex Eng aeng@redhat.com */ @@ -27,6 +28,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) @XmlType(name = "glossaryResults", propOrder = {"glossaryEntries", "warnings"}) +@Label("Glossary Results") public class GlossaryResults implements Serializable { private static final long serialVersionUID = 7100495681284134288L; private List glossaryEntries; @@ -40,6 +42,9 @@ public GlossaryResults(List glossaryEntries, List warning this.warnings = warnings; } + /** + * The list of created / updated glossary entries + */ @JsonProperty("glossaryEntries") @XmlElementWrapper(name = "glossaryEntries", namespace = Namespaces.ZANATA_API) @XmlElementRef(namespace = Namespaces.ZANATA_API) @@ -50,6 +55,9 @@ public List getGlossaryEntries() { return glossaryEntries; } + /** + * A list of warnings generated when performing the operation + */ @JsonProperty("warnings") @XmlElementWrapper(name = "warnings", namespace = Namespaces.ZANATA_API) public List getWarnings() { diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryTerm.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryTerm.java index 82798e70df..079825dcc1 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryTerm.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/GlossaryTerm.java @@ -29,6 +29,8 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -37,15 +39,15 @@ import org.zanata.common.Namespaces; /** - * + * A single glossary term for a single locale * @author Alex Eng aeng@redhat.com * - **/ - + */ @XmlType(name = "glossaryTermType", propOrder = {"comment", "content", "locale", "lastModifiedDate", "lastModifiedBy"}) @JsonPropertyOrder({ "content", "comment", "locale", "lastModifiedDate", "lastModifiedBy" }) @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Glossary Term") public class GlossaryTerm implements Serializable { /** * @@ -66,9 +68,13 @@ public class GlossaryTerm implements Serializable { public GlossaryTerm() { } + /** + * Term's locale + */ @XmlAttribute(name = "lang", namespace = Namespaces.XML) @XmlJavaTypeAdapter(type = LocaleId.class, value = LocaleIdAdapter.class) @JsonProperty("locale") + @DocumentationExample(value = "es-ES", value2 = "ja") public LocaleId getLocale() { return locale; } @@ -77,9 +83,13 @@ public void setLocale(LocaleId locale) { this.locale = locale; } + /** + * The term's translation in the given locale + */ @XmlElement(name = "content", required = false, namespace = Namespaces.ZANATA_OLD) @JsonProperty("content") + @DocumentationExample(value = "Una casa", value2 = "家") public String getContent() { return content; } @@ -98,9 +108,13 @@ public void setComment(String comment) { this.comment = comment; } + /** + * A string which identifies the user who last modififed this entry + */ @XmlElement(name = "lastModifiedBy", required = false, namespace = Namespaces.ZANATA_API) @JsonProperty("lastModifiedBy") + @DocumentationExample("homer") public String getLastModifiedBy() { return lastModifiedBy; } @@ -109,6 +123,9 @@ public void setLastModifiedBy(String lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; } + /** + * A timestamp indicating the last modification date for this entry + */ @XmlElement(name = "lastModifiedDate", required = false, namespace = Namespaces.ZANATA_API) @JsonProperty("lastModifiedDate") diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Link.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Link.java index 920e0f28ff..778ca9814d 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Link.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Link.java @@ -1,11 +1,18 @@ package org.zanata.rest.dto; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; + import java.net.URI; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; +/** + * A single link to reference a URI + */ @XmlType(name = "linkType") +@Label("Link") public class Link { private URI href; @@ -30,7 +37,11 @@ public Link(URI href, String rel, String type) { this.type = type; } + /** + * The URI reference by this link + */ @XmlAttribute(name = "href", required = true) + @DocumentationExample(value = "http://alink.com") public URI getHref() { return href; } @@ -39,6 +50,9 @@ public void setHref(URI href) { this.href = href; } + /** + * The relationship this link holds to its parent object + */ @XmlAttribute(name = "rel", required = false) public String getRel() { return rel; @@ -48,6 +62,9 @@ public void setRel(String rel) { this.rel = rel; } + /** + * The type of link + */ @XmlAttribute(name = "type", required = true) public String getType() { return type; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Links.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Links.java index e45b310a64..8f50ea375e 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Links.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Links.java @@ -1,11 +1,17 @@ package org.zanata.rest.dto; +import com.webcohesion.enunciate.metadata.Label; + import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlType; +/** + * A collection of links + */ @XmlType(name = "linksType", propOrder = {}) +@Label("Links") public class Links extends ArrayList { private static final long serialVersionUID = 1L; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/LocaleDetails.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/LocaleDetails.java index a29b9770bb..28859b8de1 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/LocaleDetails.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/LocaleDetails.java @@ -29,6 +29,8 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -40,6 +42,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonPropertyOrder({"localeId", "displayName", "alias", "nativeName", "enabled", "enabledByDefault", "pluralForms"}) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Locale Details") public class LocaleDetails implements Serializable { private static final long serialVersionUID = -8133147543880728788L; @@ -68,9 +71,13 @@ public LocaleDetails(LocaleId localeId, String displayName, String alias, this.pluralForms = pluralForms; } + /** + * Unique locale identifier + */ @XmlAttribute(name = "localeId", required = true) @XmlJavaTypeAdapter(type = LocaleId.class, value = LocaleIdAdapter.class) @NotNull + @DocumentationExample(value = "es-ES", value2 = "ja") public LocaleId getLocaleId() { return localeId; } @@ -79,7 +86,11 @@ public void setLocaleId(LocaleId localeId) { this.localeId = localeId; } + /** + * Locale's display name (in English) + */ @XmlAttribute(name = "displayName", required = true) + @DocumentationExample(value = "Spanish (Spain)", value2 = "Japanese") public String getDisplayName() { return displayName; } @@ -88,7 +99,11 @@ public void setDisplayName(String displayName) { this.displayName = displayName; } + /** + * An alternative name (if present) for this locale + */ @XmlAttribute(name = "alias", required = false) + @DocumentationExample(value = "es", value2 = "ja-JP") public String getAlias() { return alias; } @@ -98,6 +113,7 @@ public void setAlias(String alias) { } @XmlAttribute(name = "nativeName", required = false) + @DocumentationExample(value = "Español", value2 = "日本語") public String getNativeName() { return nativeName; } @@ -106,6 +122,9 @@ public void setNativeName(String nativeName) { this.nativeName = nativeName; } + /** + * Indicates whether the locale is enabled in the system or not. + */ @XmlAttribute(name = "enabled", required = true) @NotNull public boolean isEnabled() { @@ -116,6 +135,11 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } + /** + * Indicates whether the locale will be used automatically by the system. + * e.g. when creating a new project, 'enabled by default' locales will + * automatically be added to the project unless specifically indicating so. + */ @XmlAttribute(name = "enabledByDefault", required = true) @NotNull public boolean isEnabledByDefault() { @@ -126,7 +150,12 @@ public void setEnabledByDefault(boolean enabledByDefault) { this.enabledByDefault = enabledByDefault; } + /** + * A string describing the formula for the locale's plural forms + */ @XmlAttribute(name = "pluralForms", required = false) + @DocumentationExample(value = "nplurals=2; plural=(n != 1)", + value2 = "nplurals=1; plural=0") public String getPluralForms() { return pluralForms; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Project.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Project.java index 4d13b427f3..9ffc48f874 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Project.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/Project.java @@ -13,6 +13,8 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; @@ -40,6 +42,7 @@ "sourceViewURL", "sourceCheckoutURL", "links", "iterations", "status" }) @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = Inclusion.NON_NULL) +@Label("Project") public class Project implements Serializable, HasCollectionSample, HasMediaType { @@ -70,7 +73,11 @@ public Project(String id, String name, String type, String description) { this.description = description; } + /** + * The project identifier (slug) + */ @XmlAttribute(name = "id", required = true) + @DocumentationExample("my-project") public String getId() { return id; } @@ -79,8 +86,12 @@ public void setId(String id) { this.id = id; } + /** + * The project type. + */ @XmlElement(name = "defaultType", required = true, nillable = false, namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("Gettext") public String getDefaultType() { return defaultType; } @@ -93,6 +104,7 @@ public void setDefaultType(String defaultType) { @Size(max = 80) @XmlElement(name = "name", required = true, namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("My Project") public String getName() { return name; } @@ -104,6 +116,7 @@ public void setName(String name) { @Size(max = 80) @XmlElement(name = "description", required = false, namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("This is a sample project.") public String getDescription() { return description; } @@ -112,8 +125,12 @@ public void setDescription(String description) { this.description = description; } + /** + * The url to view the project's sources. + */ @XmlElement(name = "sourceViewURL", required = false, nillable = true, namespace = Namespaces.ZANATA_API) + @DocumentationExample("http://source.view.com") public String getSourceViewURL() { return sourceViewURL; } @@ -122,8 +139,12 @@ public void setSourceViewURL(String sourceViewURL) { this.sourceViewURL = sourceViewURL; } + /** + * The url where to checkout the project's sources. + */ @XmlElement(name = "sourceCheckoutURL", required = false, nillable = true, namespace = Namespaces.ZANATA_API) + @DocumentationExample("http://source.checkout.com") public String getSourceCheckoutURL() { return sourceCheckoutURL; } @@ -132,6 +153,13 @@ public void setSourceCheckoutURL(String sourceCheckoutURL) { this.sourceCheckoutURL = sourceCheckoutURL; } + /** + * Set of links managed by this project + * + * This field is ignored in PUT/POST operations + * + * @return set of Links managed by this resource + */ @XmlElement(name = "link", namespace = Namespaces.ZANATA_API) @JsonProperty("links") public Links getLinks() { @@ -149,6 +177,9 @@ public Links getLinks(boolean createIfNull) { return links; } + /** + * A list of versions (iterations) in the project + */ @XmlElementWrapper(name = "project-iterations", namespace = Namespaces.ZANATA_OLD) @XmlElementRef(namespace = Namespaces.ZANATA_OLD) @@ -167,6 +198,9 @@ public List getIterations(boolean createIfNull) { return getIterations(); } + /** + * System state of the project + */ @XmlElement(name = "status", required = false, namespace = Namespaces.ZANATA_OLD) public EntityStatus getStatus() { diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ProjectIteration.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ProjectIteration.java index 50461a6ab5..eba53b691e 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ProjectIteration.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ProjectIteration.java @@ -29,6 +29,8 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -39,12 +41,16 @@ import org.zanata.rest.MediaTypes; import org.zanata.rest.MediaTypes.Format; +/** + * Represents a Project version (or iteration). + */ @XmlType(name = "projectIterationType", propOrder = { "links", "status", "projectType" }) @XmlRootElement(name = "project-iteration") @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = Inclusion.NON_NULL) @JsonPropertyOrder({ "id", "links", "status", "projectType" }) +@Label("Project Version") public class ProjectIteration implements Serializable, HasCollectionSample, HasMediaType { @@ -64,7 +70,11 @@ public ProjectIteration(String id) { this.id = id; } + /** + * Version identifier (slug) + */ @XmlAttribute(name = "id", required = true) + @DocumentationExample("my-iteration") public String getId() { return id; } @@ -107,8 +117,13 @@ public void setStatus(EntityStatus status) { this.status = status; } + /** + * The type of project version. This type could be overriding the project's + * original type. + */ @XmlElement(name = "projectType", required = false, namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("Podir") public String getProjectType() { return projectType; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/QualifiedName.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/QualifiedName.java index cbf04aa5af..7101eb832c 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/QualifiedName.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/QualifiedName.java @@ -4,6 +4,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -11,7 +13,7 @@ import org.zanata.rest.MediaTypes; /** - * Object Glossary qualified name. Usage: + * Describes a qualified system name. Usage: * {@link GlossaryEntry#getQualifiedName()} * {@link org.zanata.rest.service.GlossaryResource} * @@ -20,6 +22,7 @@ @XmlRootElement(name = "qualifiedName") @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Qualified Name") public class QualifiedName implements Serializable, HasMediaType { private static final long serialVersionUID = 934216980812012602L; private String name; @@ -34,6 +37,7 @@ public QualifiedName(String name) { @XmlElement(name = "name", required = false, namespace = Namespaces.ZANATA_API) @JsonProperty("name") + @DocumentationExample("global/default") public String getName() { return name; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ResultList.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ResultList.java index 7a705ecc37..b39842c846 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ResultList.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/ResultList.java @@ -4,16 +4,19 @@ import java.util.ArrayList; import java.util.List; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; /** + * A list of results * @author Alex Eng aeng@redhat.com */ @JsonPropertyOrder({ "results", "totalCount" }) @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Result List") public class ResultList implements Serializable { private static final long serialVersionUID = -2149554068631922866L; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/SearchResult.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/SearchResult.java index eaf12f0111..5a1431ab2e 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/SearchResult.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/SearchResult.java @@ -38,7 +38,8 @@ public enum SearchResultType { Project, LanguageTeam, Person, - Group; + Group, + ProjectVersion } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/VersionInfo.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/VersionInfo.java index 2f099ecd83..b5182d9812 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/VersionInfo.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/VersionInfo.java @@ -6,19 +6,22 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonTypeName; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.Namespaces; /** - * Holds version info + * Holds system version information */ @XmlRootElement(name = "versionInfo") @XmlType(name = "versionType", propOrder = { "versionNo", "buildTimeStamp", "scmDescribe" }) @JsonTypeName(value = "versionType") @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Version Info") public final class VersionInfo implements Serializable { private static final long serialVersionUID = 1L; private String versionNo; @@ -43,16 +46,27 @@ public VersionInfo(VersionInfo other) { this(other.versionNo, other.buildTimeStamp, other.scmDescribe); } + /** + * Version number + */ @XmlElement(name = "versionNo", namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("4.0.0") public String getVersionNo() { return versionNo; } + /** + * ISO8601 timestamp for when the current system version was built + */ @XmlElement(name = "buildTimeStamp", namespace = Namespaces.ZANATA_OLD) + @DocumentationExample("20170225-1448") public String getBuildTimeStamp() { return buildTimeStamp; } + /** + * Identifier for the current version in source control + */ @XmlElement(name = "scmDescribe", namespace = Namespaces.ZANATA_API) public String getScmDescribe() { return scmDescribe; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/Resource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/Resource.java index 4a7b63192b..047ed55dc6 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/Resource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/Resource.java @@ -1,21 +1,19 @@ package org.zanata.rest.dto.resource; -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -import com.webcohesion.enunciate.metadata.DocumentationExample; -import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.Namespaces; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + /** * A series of text flows to be translated and sharing common metadata. */ @@ -24,7 +22,7 @@ @JsonPropertyOrder({ "name", "contentType", "lang", "extensions", "textFlows" }) @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) -@ResourceLabel("Resource") +@Label("Resource") public class Resource extends AbstractResourceMeta { private static final long serialVersionUID = 1L; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/TranslationsResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/TranslationsResource.java index c90fdce691..8a4a98f8e8 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/TranslationsResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/resource/TranslationsResource.java @@ -10,6 +10,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; @@ -31,6 +32,7 @@ @JsonPropertyOrder({ "links", "extensions", "textFlowTargets" }) @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@Label("Translations") public class TranslationsResource implements Serializable, HasSample, Extensible { @@ -40,6 +42,9 @@ public class TranslationsResource implements Serializable, private List textFlowTargets; private Integer revision; + /** + * Any provided extensions + */ @XmlElementWrapper(name = "extensions", required = false, namespace = Namespaces.ZANATA_OLD) @XmlElement(name = "extension", namespace = Namespaces.ZANATA_OLD) @@ -60,6 +65,9 @@ public ExtensionSet getExtensions( return extensions; } + /** + * The text flow targets (i.e. translated text) + */ @XmlElementWrapper(name = "targets", required = false, namespace = Namespaces.ZANATA_OLD) @XmlElement(name = "text-flow-target", namespace = Namespaces.ZANATA_API) @@ -71,6 +79,9 @@ public List getTextFlowTargets() { return textFlowTargets; } + /** + * A collection of links provided with the translations. + */ @XmlElement(name = "links", namespace = Namespaces.ZANATA_OLD) public Links getLinks() { return links; @@ -87,6 +98,9 @@ public Links getLinks(boolean createIfNull) { return links; } + /** + * Revision number for the translations + */ @XmlAttribute() public Integer getRevision() { return revision; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/ContainerTranslationStatistics.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/ContainerTranslationStatistics.java index 1b38fbbc90..97cb9c0562 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/ContainerTranslationStatistics.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/ContainerTranslationStatistics.java @@ -29,6 +29,8 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.zanata.rest.dto.Link; @@ -45,6 +47,7 @@ @XmlRootElement(name = "containerStats") @JsonIgnoreProperties(ignoreUnknown = true) @JsonPropertyOrder({ "id", "refs", "stats", "detailedStats" }) +@Label("Container Translation Statistics") public class ContainerTranslationStatistics implements Serializable { private static final long serialVersionUID = 1L; private String id; @@ -60,6 +63,7 @@ public ContainerTranslationStatistics() { * etc). */ @XmlAttribute + @DocumentationExample("my-project") public String getId() { return id; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/TranslationStatistics.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/TranslationStatistics.java index f029125bf5..8f47fb3b82 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/TranslationStatistics.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/TranslationStatistics.java @@ -29,6 +29,8 @@ import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -53,6 +55,7 @@ @JsonPropertyOrder({ "total", "untranslated", "needReview", "translated", "approved", "rejected", "readyForReview", "fuzzy", "unit", "locale", "lastTranslated" }) +@Label("Translation Statistics") public class TranslationStatistics implements Serializable { private static final long serialVersionUID = 1L; private StatUnit unit; @@ -111,6 +114,7 @@ public TranslationStatistics(TransUnitWords wordCount, String locale) { * Number of untranslated elements. */ @XmlAttribute + @DocumentationExample("25") public long getUntranslated() { return translationCount.getUntranslated(); } @@ -147,6 +151,7 @@ public long getNeedReview() { * @return */ @XmlAttribute + @DocumentationExample("50") public long getFuzzy() { return translationCount.getNeedReview(); } @@ -179,6 +184,7 @@ public long getTranslatedAndApproved() { * @return number of translated but not yet approved elements. */ @XmlAttribute + @DocumentationExample("30") public long getTranslatedOnly() { return translationCount.getTranslated(); } @@ -191,6 +197,7 @@ public void setTranslatedOnly(long translatedOnly) { * @return Number of approved elements. */ @XmlAttribute + @DocumentationExample("80") public long getApproved() { return translationCount.getApproved(); } @@ -200,6 +207,7 @@ public void setApproved(long approved) { } @XmlAttribute + @DocumentationExample("10") public long getRejected() { return translationCount.getRejected(); } @@ -242,6 +250,7 @@ public void setUnit(StatUnit unit) { * Locale for the translation statistics. */ @XmlAttribute + @DocumentationExample("es-ES") public String getLocale() { return locale; } @@ -250,7 +259,11 @@ public void setLocale(String locale) { this.locale = locale; } + /** + * Last translation information. Includes date and user. + */ @XmlAttribute + @DocumentationExample("31/12/15 23:59 by homer") public String getLastTranslated() { return lastTranslated; } @@ -341,6 +354,7 @@ public String toString() { return sb.toString(); } + @Label("Stats Unit") public enum StatUnit { /** Statistics are measured in words. */ WORD, diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/BaseContributionStatistic.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/BaseContributionStatistic.java index 8ea3827ac2..615b07da74 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/BaseContributionStatistic.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/BaseContributionStatistic.java @@ -1,5 +1,6 @@ package org.zanata.rest.dto.stats.contribution; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; @@ -15,6 +16,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) @JsonPropertyOrder({ "approved", "rejected", "translated", "needReview" }) +@Label("Base Contribution Statistics") public class BaseContributionStatistic implements Serializable { private static final long serialVersionUID = 6615374806881888982L; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/ContributionStatistics.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/ContributionStatistics.java index dd34f696bb..dec1e83020 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/ContributionStatistics.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/ContributionStatistics.java @@ -4,15 +4,20 @@ import java.util.ArrayList; import java.util.List; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; /** + * Contains information about translations contributed by a single user. + * * @author Alex Eng aeng@redhat.com */ @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) @JsonPropertyOrder({ "username", "contributions" }) +@Label("Contribution Statistics") public class ContributionStatistics implements Serializable { private static final long serialVersionUID = -6328249224235406066L; @@ -29,7 +34,11 @@ public ContributionStatistics(String username, this.contributions = contributions; } + /** + * User name responsible for the contributions. + */ @JsonProperty("username") + @DocumentationExample("bart") public String getUsername() { return username; } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/LocaleStatistics.java b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/LocaleStatistics.java index 60a38b7a8e..da99d8ec06 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/LocaleStatistics.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/dto/stats/contribution/LocaleStatistics.java @@ -2,16 +2,21 @@ import java.io.Serializable; +import com.webcohesion.enunciate.metadata.DocumentationExample; +import com.webcohesion.enunciate.metadata.Label; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.LocaleId; /** + * Contains translation statistics for a single locale. + * * @author Alex Eng aeng@redhat.com */ @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) @JsonPropertyOrder({ "locale", "translation-stats", "review-stats" }) +@Label("Locale Statistics") public class LocaleStatistics implements Serializable { private static final long serialVersionUID = 711345550950903773L; @@ -36,16 +41,26 @@ public LocaleStatistics(LocaleId locale, private BaseContributionStatistic reviewStats; + /** + * Locale code for for the stats + */ @JsonProperty("locale") + @DocumentationExample(value = "es-ES", value2 = "ja") public LocaleId getLocale() { return locale; } + /** + * Contains translation statistics only. + */ @JsonProperty("translation-stats") public BaseContributionStatistic getTranslationStats() { return translationStats; } + /** + * Contains review statistics only. + */ @JsonProperty("review-stats") public BaseContributionStatistic getReviewStats() { return reviewStats; diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/CopyTransResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/CopyTransResource.java index 4138bcbbbf..70be790686 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/CopyTransResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/CopyTransResource.java @@ -29,6 +29,8 @@ import javax.ws.rs.core.MediaType; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.zanata.rest.dto.CopyTransStatus; import com.webcohesion.enunciate.metadata.rs.TypeHint; @@ -55,26 +57,23 @@ public interface CopyTransResource extends RestResource { * Project version identifier * @param docId * Document Id to copy translations into. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Translation copy was started for the given document. - * The status of the process is also returned in the response - * contents.
- * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. Translation copy will - * not start in this case. */ @POST @Path("/proj/{projectSlug}/iter/{iterationSlug}/doc/{docId:.+}") // /copytrans/proj/{projectSlug}/iter/{iterationSlug}/doc/{docId} - @TypeHint(CopyTransStatus.class) - public - CopyTransStatus startCopyTrans( - @PathParam("projectSlug") String projectSlug, - @PathParam("iterationSlug") String iterationSlug, - @PathParam("docId") String docId); + @TypeHint(CopyTransStatus.class) + @StatusCodes({ + @ResponseCode(code = 200, + condition = "Translation copy was started for the given document. " + + "The status of the process is also returned in the response's body."), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) + public + CopyTransStatus startCopyTrans( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @PathParam("docId") String docId); /** * Retrieves the status for a Translation copy process for a document. @@ -85,22 +84,21 @@ CopyTransStatus startCopyTrans( * Project version identifier * @param docId * Document Id - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - A Translation copy process was found, and its status - * will be returned in the body of the response.
- * NOT_FOUND(404) - If there is no record of a recent translation - * copy process for the specified document. INTERNAL SERVER - * ERROR(500) - If there is an unexpected error in the server while - * performing this operation. */ @GET @Path("/proj/{projectSlug}/iter/{iterationSlug}/doc/{docId:.+}") // /copytrans/proj/{projectSlug}/iter/{iterationSlug}/doc/{docId} - @TypeHint(CopyTransStatus.class) - public - CopyTransStatus getCopyTransStatus( - @PathParam("projectSlug") String projectSlug, - @PathParam("iterationSlug") String iterationSlug, - @PathParam("docId") String docId); + @TypeHint(CopyTransStatus.class) + @StatusCodes({ + @ResponseCode(code = 200, + condition = "A translation copy process was found, and its " + + "status will be returned in the body of the response"), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) + public + CopyTransStatus getCopyTransStatus( + @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @PathParam("docId") String docId); } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/GlossaryResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/GlossaryResource.java index 0700c025e1..8f1f0e38cc 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/GlossaryResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/GlossaryResource.java @@ -37,6 +37,8 @@ import javax.ws.rs.core.Response; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; import org.zanata.common.LocaleId; import org.zanata.rest.GlossaryFileUploadForm; @@ -50,7 +52,7 @@ import com.webcohesion.enunciate.metadata.rs.TypeHint; /** - * Glossary management via REST + * Glossary management * @author Alex Eng aeng@redhat.com * **/ @@ -63,6 +65,10 @@ MediaTypes.APPLICATION_ZANATA_GLOSSARY_XML, MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON }) @ResourceLabel("Glossary") +@StatusCodes({ + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") +}) public interface GlossaryResource extends RestResource { public static final String SERVICE_PATH = "/glossary"; @@ -78,13 +84,6 @@ public interface GlossaryResource extends RestResource { /** * Return default global glossary qualifiedName - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - List of Global glossary qualified names used in the system. - * e.g {@link #GLOBAL_QUALIFIED_NAME} - * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Path("/qualifiedName") @@ -92,6 +91,10 @@ public interface GlossaryResource extends RestResource { MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @TypeHint(QualifiedName.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Returns the qualified name " + + "for the system-wide glossary"), + }) public Response getQualifiedName(); /** @@ -99,12 +102,6 @@ public interface GlossaryResource extends RestResource { * * @param qualifiedName * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Global glossary info in the system. - * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Path("/info") @@ -112,12 +109,19 @@ public interface GlossaryResource extends RestResource { MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @TypeHint(GlossaryInfo.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Returns the global glossary " + + "information"), + }) public Response getInfo( @DefaultValue(GLOBAL_QUALIFIED_NAME) @QueryParam("qualifiedName") String qualifiedName); /** - * Returns Glossary entries for the given source and translation locale with - * paging + * Returns a subset of Glossary entries for the given source and translation + * locale as indicated by the paging parameters. + * + * @see {@link org.zanata.rest.dto.GlossaryEntry} for details on the result + * list's contents. * * @param srcLocale * Source locale - Required (default value: en-US). @@ -135,19 +139,18 @@ public Response getInfo( * See {@link org.zanata.common.GlossarySortField} * @param qualifiedName * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing all Glossary entries for the given - * locale. - * Bad request(400) - If page or sizePerPage is negative value, or sizePerPage is more than 1000. - * INTERNAL SERVER ERROR(500) - If there is an unexpected - * error in the server while performing this operation. */ @GET @Path("/entries") @Produces({ MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, MediaType.APPLICATION_JSON }) @TypeHint(ResultList.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Response containing Glossary " + + "entries for the given locale."), + @ResponseCode(code = 400, condition = "If page or sizePerPage are " + + "negative, or sizePerPage is greater than 1000"), + }) public Response getEntries( @DefaultValue("en-US") @QueryParam("srcLocale") LocaleId srcLocale, @QueryParam("transLocale") LocaleId transLocale, @@ -160,6 +163,9 @@ public Response getEntries( /** * Returns Glossary entries based on a fuzzy text search. * + * @see {@link org.zanata.rest.dto.GlossaryEntry} for details on the result + * list's contents. + * * @param srcLocale * Source locale * @param transLocale @@ -172,23 +178,20 @@ public Response getEntries( * @param projectSlug * (optional) Project slug if a project glossary should be searched * in addition to the global glossary. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing all Glossary entries for the given - * locale. - * Bad request(400) - * - When maxResults is not strictly positive or is more than 1000. - * - When searchText is missing - * - When transLocale is missing - * - When there is an error parsing the searchText - * INTERNAL SERVER ERROR(500) - If there is an unexpected - * error in the server while performing this operation. */ @GET @Path("/search") @Produces({ MediaTypes.APPLICATION_ZANATA_GLOSSARY_JSON, MediaType.APPLICATION_JSON }) @TypeHint(ResultList.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Response containing Glossary " + + "entries for the given search parameters."), + @ResponseCode(code = 400, condition = "When maxResults is not strictly positive or is more than 1000"), + @ResponseCode(code = 400, condition = "When searchText is missing"), + @ResponseCode(code = 400, condition = "When transLocale is missing"), + @ResponseCode(code = 400, condition = "When there is an error parsing the searchText"), + }) Response search( @DefaultValue("en-US") @QueryParam("srcLocale") LocaleId srcLocale, @CheckForNull @QueryParam("transLocale") LocaleId transLocale, @@ -204,7 +207,7 @@ Response search( * @param locale include locale-specific detail for this locale * @param termIds id for glossary terms in the default locale, found in * results of {@link #search(LocaleId, LocaleId, int, String, String)} - * @return source and target glossary details. + * @return Source and target glossary details. */ @GET @Path("/details/{locale}") @@ -213,111 +216,122 @@ Response search( // TODO when GWT is removed, move the GlossaryDetails class to this module // and add the type hint. // @TypeHint(GlossaryDetails[].class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Details successfully found") + }) Response getDetails( @CheckForNull @PathParam("locale") LocaleId locale, @CheckForNull @QueryParam("termIds") List termIds); /** - * Download all glossary entries as file + * Download all glossary entries as a file * - * @param fileType - po or cvs (case insensitive). Default - csv - * @param locales - optional comma separated list of languages required. + * @param fileType 'po' or 'csv' (case insensitive) are currently supported + * @param locales optional comma separated list of languages required. * @param qualifiedName * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} + * @return A file stream with the glossary contents in the specified format */ @GET @Path("/file") @Produces(MediaType.APPLICATION_OCTET_STREAM) + @StatusCodes({ + @ResponseCode(code = 200, condition = "File is successfully built " + + "and served") + }) public Response downloadFile( @DefaultValue("csv") @QueryParam("fileType") String fileType, @QueryParam("locales") String locales, @DefaultValue(GLOBAL_QUALIFIED_NAME) @QueryParam("qualifiedName") String qualifiedName); /** - * Create or update glossary entry. - * GlossaryTerm with locale different from {@param locale} will be ignored. + * Create or update glossary entries. + * Glossary Terms with a locale different from the given locale parameter + * will be ignored. * * @param glossaryEntries The glossary entries to create/update * @param qualifiedName - * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} + * Qualified name of glossary, defaults to {@link #GLOBAL_QUALIFIED_NAME} * @param locale - * The translation locale to create/update - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - If the glossary entry were successfully created/updated. - * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. + * Locale to which the given glossary entries belong */ @POST @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Path("/entries") @TypeHint(GlossaryResults.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "The glossary entries were " + + "successfully created or updated"), + @ResponseCode(code = 401, condition = "The user does not have the proper" + + " permissions to perform this operation") + }) public Response post(List glossaryEntries, @QueryParam("locale") String locale, @DefaultValue(GLOBAL_QUALIFIED_NAME) @QueryParam("qualifiedName") String qualifiedName); /** - * Upload glossary file (po, cvs) - * - * @param form {@link org.zanata.rest.GlossaryFileUploadForm} + * Upload glossary file (currently supported formats: po, csv) * - * @return The following response status codes will be returned from this - * operation:
- * CREATED(201) - If files successfully uploaded. - * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. * + * @param form Multi-part form with the following named parts:
+ * file: The file contents
+ * srcLocale: Source locale for the glossary entries
+ * transLocale: Translation locale for the glossary entries
+ * fileName: The name of the file being uploaded
+ * qualifiedName: The qualified name for the glossary
*/ @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_JSON) @POST @TypeHint(GlossaryResults.class) + @StatusCodes({ + @ResponseCode(code = 201, condition = "Files successfully uploaded"), + @ResponseCode(code = 401, condition = "The user does not have the proper" + + " permissions to perform this operation") + }) public Response upload(@MultipartForm GlossaryFileUploadForm form); /** * - * Delete glossary which given id. + * Delete a glossary entry. * * @param id id for source glossary term * @param qualifiedName - * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - If the glossary entry were successfully deleted. - * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. + * Qualified name of glossary, defaults to {@link #GLOBAL_QUALIFIED_NAME} + * @return the removed glossary entry */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/entries/{id}") @TypeHint(GlossaryEntry.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "The glossary entry was successfully deleted"), + @ResponseCode(code = 401, condition = "The user does not have the proper" + + " permissions to perform this operation") + }) public Response deleteEntry(@PathParam("id") Long id, @DefaultValue(GLOBAL_QUALIFIED_NAME) @QueryParam("qualifiedName") String qualifiedName); /** - * Delete all glossary terms. + * Delete all entries in a glossary. * * @param qualifiedName - * Qualified name of glossary, default to {@link #GLOBAL_QUALIFIED_NAME} + * Qualified name of glossary, defaults to {@link #GLOBAL_QUALIFIED_NAME} + * + * @return The number of deleted glossary entries + * + * @responseExample * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - If the glossary entries were successfully deleted. - * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. + * TODO Need to define a 'produces' header */ @DELETE @TypeHint(Integer.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "The glossary was deleted"), + @ResponseCode(code = 401, condition = "The user does not have the proper" + + " permissions to perform this operation") + }) public Response deleteAllEntries( @DefaultValue(GLOBAL_QUALIFIED_NAME) @QueryParam("qualifiedName") String qualifiedName); diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectIterationLocalesResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectIterationLocalesResource.java index deedd6df32..e9327a9966 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectIterationLocalesResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectIterationLocalesResource.java @@ -50,6 +50,10 @@ public interface ProjectIterationLocalesResource extends RestResource { * * This may be the list of locales inherited from the project. * + * This also returns locale aliases. + * + * @see ProjectVersionResource#getLocales(String, String) + * * @return * OK 200 containing the list of LocaleDetails * NOT FOUND 404 if the project-version does not exist diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectVersionResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectVersionResource.java index c2e038b4de..d0066ec31e 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectVersionResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectVersionResource.java @@ -191,6 +191,8 @@ public Response getContributors( /** * Retrieves a full list of locales enabled in project version. * + * @see ProjectIterationLocalesResource#get() + * * @param projectSlug * Project identifier * @param versionSlug diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectsResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectsResource.java index 1f42452fd6..0f3e55a6fe 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectsResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/ProjectsResource.java @@ -29,6 +29,8 @@ import javax.ws.rs.core.Response; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.zanata.rest.MediaTypes; import org.zanata.rest.dto.Project; @@ -49,22 +51,20 @@ public interface ProjectsResource extends RestResource { public static final String SERVICE_PATH = "/projects"; /** - * Retrieves a full list of projects in the system. The result is - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing a full list of projects. The list - * will be wrapped in a "projects" element, and all its child - * elements will be "project"s.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. + * Retrieves a full list of projects in the system. */ @GET @Produces({ MediaTypes.APPLICATION_ZANATA_PROJECTS_XML, MediaTypes.APPLICATION_ZANATA_PROJECTS_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - @TypeHint(Project[].class) - public - Response get(); + @TypeHint(Project[].class) + @StatusCodes({ + @ResponseCode(code = 200, + condition = "Response containing a full list of projects."), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) + public + Response get(); } diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/StatisticsResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/StatisticsResource.java index dd11254e4e..3343bc336e 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/StatisticsResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/StatisticsResource.java @@ -29,8 +29,9 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.zanata.rest.dto.ProjectStatisticsMatrix; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; import org.zanata.rest.dto.stats.contribution.ContributionStatistics; @@ -69,18 +70,18 @@ public interface StatisticsResource extends RestResource { * Locale statistics to be fetched. If this is empty, all locale * statistics will be returned. This parameter may be specified * multiple times if multiple locales are to be fetched. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing translation statistics for the - * specified parameters.
- * NOT FOUND(404) - If a project iteration could not be found for - * the given parameters.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Path("/proj/{projectSlug}/iter/{iterationSlug}") @TypeHint(ContainerTranslationStatistics.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Contains translation statistics" + + " for the specified parameters"), + @ResponseCode(code = 404, condition = "A project iteration could " + + "not be found for the given parameters"), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) public ContainerTranslationStatistics getStatistics( @@ -106,18 +107,18 @@ public interface StatisticsResource extends RestResource { * Locale statistics to be fetched. If this is empty, all locale * statistics will be returned. This parameter may be specified * multiple times if multiple locales are to be fetched. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing translation statistics for the - * specified parameters.
- * NOT FOUND(404) - If a document could not be found for the given - * parameters.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Path("/proj/{projectSlug}/iter/{iterationSlug}/doc/{docId:.*}") @TypeHint(ContainerTranslationStatistics.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Contains translation statistics" + + " for the specified parameters"), + @ResponseCode(code = 404, condition = "A document could " + + "not be found for the given parameters"), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) public ContainerTranslationStatistics getStatistics( @@ -139,23 +140,21 @@ public interface StatisticsResource extends RestResource { * username of contributor * @param dateRange * date range from..to (yyyy-mm-dd..yyyy-mm-dd) - * * @param includeAutomatedEntry * whether to include automatic entries of translation into statistic - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response containing contribution statistics for the - * specified parameters.
- * BAD REQUEST(400) - If dateRange param is invalid.
- * NOT FOUND(404) - If a version or user could not be found.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Path("/project/{projectSlug}/version/{versionSlug}/contributor/{username}/{dateRange}") @TypeHint(ContributionStatistics.class) @Produces({ MediaType.APPLICATION_JSON }) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Contains contribution statistics" + + " for the specified parameters"), + @ResponseCode(code = 404, condition = "A project version could " + + "not be found for the given parameters"), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) public ContributionStatistics getContributionStatistics( @PathParam("projectSlug") String projectSlug, diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/TranslatedDocResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/TranslatedDocResource.java index 3cb76402b2..02dbbaee00 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/TranslatedDocResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/TranslatedDocResource.java @@ -40,6 +40,8 @@ import javax.ws.rs.core.Response; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.jboss.resteasy.util.HttpHeaderNames; import org.zanata.common.LocaleId; import org.zanata.rest.dto.resource.TranslationsResource; @@ -61,6 +63,10 @@ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @ResourceLabel("Translated Documents") +@StatusCodes({ + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") +}) public interface TranslatedDocResource extends RestResource { @SuppressWarnings("deprecation") public static final String SERVICE_PATH = @@ -86,32 +92,29 @@ public interface TranslatedDocResource extends RestResource { * An Entity tag identifier. Based on this identifier (if * provided), the server will decide if it needs to send a * response to the client or not (See return section). - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Successfully retrieved translations. The data will be - * contained in the response.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters. Also if no - * translations are found for the given document and locale.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation.
- * NOT_MODIFIED(304) - If the provided ETag matches the server's - * stored ETag, it will reply with this code, indicating that the - * last received response is still valid and should be reused. - * * Deprecated. Use {@link #getTranslationsWithDocId} */ - @Deprecated @GET @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") // /r/{id}/translations/{locale} - @TypeHint(TranslationsResource.class) - public - Response getTranslations(@PathParam("id") String idNoSlash, - @PathParam("locale") LocaleId locale, - @QueryParam("ext") Set extensions, - @QueryParam("skeletons") boolean createSkeletons, - @HeaderParam(HttpHeaderNames.IF_NONE_MATCH) String eTag); + @TypeHint(TranslationsResource.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Successfully retrieved translations. " + + "The translation data will be contained in the response."), + @ResponseCode(code = 404, condition = "The project, version, or document could" + + " not be found with the given parameters. Also, if no translations are" + + " found for the given document and locale."), + @ResponseCode(code = 304, condition = "If the provided ETag matches the server's" + + " stored ETag, indicating that the last received response is still valid" + + " and should be reused."), + }) + @Deprecated + public + Response getTranslations(@PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + @QueryParam("ext") Set extensions, + @QueryParam("skeletons") boolean createSkeletons, + @HeaderParam(HttpHeaderNames.IF_NONE_MATCH) String eTag); /** * Retrieves a set of translations for a given locale. @@ -123,29 +126,27 @@ Response getTranslations(@PathParam("id") String idNoSlash, * @param extensions * The translation extensions to retrieve (e.g. "comment"). This * parameter allows multiple values. - * @param skeletons + * @param createSkeletons * Indicates whether to generate untranslated entries or not. * @param eTag * An Entity tag identifier. Based on this identifier (if * provided), the server will decide if it needs to send a * response to the client or not (See return section). - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Successfully retrieved translations. The data will be - * contained in the response.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters. Also if no - * translations are found for the given document and locale.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation.
- * NOT_MODIFIED(304) - If the provided ETag matches the server's - * stored ETag, it will reply with this code, indicating that the - * last received response is still valid and should be reused. */ @GET @Path(RESOURCE_PATH + "/translations/{locale}") // /r/resource/translations/{locale} @TypeHint(TranslationsResource.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Successfully retrieved translations. " + + "The translation data will be contained in the response."), + @ResponseCode(code = 404, condition = "The project, version, or document could" + + " not be found with the given parameters. Also, if no translations are" + + " found for the given document and locale."), + @ResponseCode(code = 304, condition = "If the provided ETag matches the server's" + + " stored ETag, indicating that the last received response is still valid" + + " and should be reused."), + }) public Response getTranslationsWithDocId( @PathParam("locale") LocaleId locale, @QueryParam("id") @DefaultValue("") String id, @@ -166,25 +167,23 @@ public Response getTranslationsWithDocId( * (','). * @param locale * The locale for which to get translations. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Successfully deleted the translations.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters. UNAUTHORIZED(401) - - * If the user does not have the proper permissions to perform this - * operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. - * * Deprecated. Use {@link #deleteTranslationsWithDocId} */ @Deprecated @DELETE @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + @TypeHint(TypeHint.NO_CONTENT.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Successfully deleted the translations."), + @ResponseCode(code = 404, condition = "If a project, project iteration or document" + + " could not be found with the given parameters."), + @ResponseCode(code = 401, condition = "If the user does not have the proper" + + " permissions to perform this operation."), + }) // /r/{id}/translations/{locale} - public - Response deleteTranslations(@PathParam("id") String idNoSlash, - @PathParam("locale") LocaleId locale); + public + Response deleteTranslations(@PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale); /** * Deletes a set of translations for a given locale. Also deletes any @@ -195,18 +194,17 @@ Response deleteTranslations(@PathParam("id") String idNoSlash, * The document identifier. * @param locale * The locale for which to get translations. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Successfully deleted the translations.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters. UNAUTHORIZED(401) - - * If the user does not have the proper permissions to perform this - * operation.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @DELETE @Path(RESOURCE_PATH + "/translations/{locale}") + @TypeHint(TypeHint.NO_CONTENT.class) + @StatusCodes({ + @ResponseCode(code = 200, condition = "Successfully deleted the translations."), + @ResponseCode(code = 404, condition = "If a project, project iteration or document" + + " could not be found with the given parameters."), + @ResponseCode(code = 401, condition = "If the user does not have the proper" + + " permissions to perform this operation."), + }) // /r/resource/translations/{locale} public Response deleteTranslationsWithDocId( @PathParam("locale") LocaleId locale, @@ -235,31 +233,29 @@ public Response deleteTranslationsWithDocId( * Auto will check the history of your translations and will not * overwrite any translations for which it detects a previous * value is being pushed. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Translations were successfully updated.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters.
- * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * BAD REQUEST(400) - If there are problems with the parameters - * passed. i.e. Merge type is not one of the accepted types. This - * response should have a content message indicating a reason.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. - * * Deprecated. Use {@link #putTranslationsWithDocId} */ @Deprecated @PUT @Path(RESOURCE_SLUG_TEMPLATE + "/translations/{locale}") + @StatusCodes({ + @ResponseCode(code = 200, condition = "Translations were successfully updated."), + @ResponseCode(code = 404, condition = "If a project, project iteration or document" + + " could not be found with the given parameters."), + @ResponseCode(code = 401, condition = "If the user does not have the proper" + + " permissions to perform this operation."), + @ResponseCode(code = 400, condition = "If there are problems with the passed parameters." + + " e.g. Merge type is not one of the accepted types. This response should have a" + + " content message indicating a reason.", + type = @TypeHint(String.class)) + }) // /r/{id}/translations/{locale} - public - Response putTranslations(@PathParam("id") String idNoSlash, - @PathParam("locale") LocaleId locale, - TranslationsResource messageBody, - @QueryParam("ext") Set extensions, - @QueryParam("merge") @DefaultValue("auto") String merge); + public + Response putTranslations(@PathParam("id") String idNoSlash, + @PathParam("locale") LocaleId locale, + TranslationsResource messageBody, + @QueryParam("ext") Set extensions, + @QueryParam("merge") @DefaultValue("auto") String merge); /** * Updates the translations for a document and a locale. @@ -280,21 +276,20 @@ Response putTranslations(@PathParam("id") String idNoSlash, * Auto will check the history of your translations and will not * overwrite any translations for which it detects a previous * value is being pushed. - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Translations were successfully updated.
- * NOT FOUND(404) - If a project, project iteration or document - * could not be found with the given parameters.
- * UNAUTHORIZED(401) - If the user does not have the proper - * permissions to perform this operation.
- * BAD REQUEST(400) - If there are problems with the parameters - * passed. i.e. Merge type is not one of the accepted types. This - * response should have a content message indicating a reason.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @PUT @Path(RESOURCE_PATH + "/translations/{locale}") + @StatusCodes({ + @ResponseCode(code = 200, condition = "Translations were successfully updated."), + @ResponseCode(code = 404, condition = "If a project, project iteration or document" + + " could not be found with the given parameters."), + @ResponseCode(code = 401, condition = "If the user does not have the proper" + + " permissions to perform this operation."), + @ResponseCode(code = 400, condition = "If there are problems with the passed parameters." + + " e.g. Merge type is not one of the accepted types. This response should have a" + + " content message indicating a reason.", + type = @TypeHint(String.class)) + }) // /r/resource/translations/{locale} public Response putTranslationsWithDocId( @PathParam("locale") LocaleId locale, diff --git a/api/zanata-common-api/src/main/java/org/zanata/rest/service/VersionResource.java b/api/zanata-common-api/src/main/java/org/zanata/rest/service/VersionResource.java index ee8ca1d23e..166840a848 100644 --- a/api/zanata-common-api/src/main/java/org/zanata/rest/service/VersionResource.java +++ b/api/zanata-common-api/src/main/java/org/zanata/rest/service/VersionResource.java @@ -28,6 +28,8 @@ import javax.ws.rs.core.Response; import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.zanata.rest.MediaTypes; import org.zanata.rest.dto.VersionInfo; @@ -44,18 +46,18 @@ public interface VersionResource extends RestResource { /** * Retrieve Version information for the application. - * - * @return The following response status codes will be returned from this - * operation:
- * OK(200) - Response with the system's version information in the - * content.
- * INTERNAL SERVER ERROR(500) - If there is an unexpected error in - * the server while performing this operation. */ @GET @Produces({ MediaTypes.APPLICATION_ZANATA_VERSION_XML, MediaTypes.APPLICATION_ZANATA_VERSION_JSON }) @TypeHint(VersionInfo.class) + @StatusCodes({ + @ResponseCode(code = 200, + condition = "Response with the system's version information in " + + "the body"), + @ResponseCode(code = 500, + condition = "If there is an unexpected error in the server while performing this operation") + }) public Response get(); } diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000000..b3a3a3adf2 --- /dev/null +++ b/client/README.md @@ -0,0 +1,10 @@ +Holds most of Zanata's client code, including the Zanata Maven Plugin and Zanata CLI, but not the python client. +It also contains client-side bindings for interacting with a Zanata server's REST API. + +[![license](https://img.shields.io/github/license/zanata/zanata-platform.svg?maxAge=3600)](https://github.com/zanata/zanata-platform/blob/master/LICENSE) + +Latest version of zanata-cli: +[![Maven Central](https://img.shields.io/maven-central/v/org.zanata/zanata-cli.svg)](https://maven-badges.herokuapp.com/maven-central/org.zanata/zanata-cli) + +Latest version of zanata-maven-plugin: +[![Maven Central](https://img.shields.io/maven-central/v/org.zanata/zanata-maven-plugin.svg)](https://maven-badges.herokuapp.com/maven-central/org.zanata/zanata-maven-plugin) diff --git a/client/README.txt b/client/README.txt deleted file mode 100644 index 9722d79443..0000000000 --- a/client/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Holds most of Zanata's client code, including the Zanata Maven Plugin and Zanata CLI, but not the python client. -It also contains REST stub for interacting with a Zanata server. diff --git a/license-gpl.md b/license-gpl.md new file mode 100644 index 0000000000..00c115f306 --- /dev/null +++ b/license-gpl.md @@ -0,0 +1,361 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at +all. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work +based on the Program" means either the Program or any derivative work +under copyright law: that is to say, a work containing the Program or +a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee +is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +**1.** You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Program or any +portion of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + +**a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + + +**b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + + +**c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such interactive +use in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and telling +the user how to view a copy of this License. (Exception: if the +Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + +**a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; or, + + +**b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete machine-readable +copy of the corresponding source code, to be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + + +**c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and +will automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +**6.** Each time you redistribute the Program (or any work based on +the Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +**7.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Program at all. For +example, if a patent license would not permit royalty-free +redistribution of the Program by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**8.** If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**9.** The Free Software Foundation may publish revised and/or new +versions of the 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 a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +**10.** If you wish to incorporate parts of the Program into other +free programs whose distribution conditions are different, write to +the author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +**NO WARRANTY** + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE 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. + +### 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 +convey 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 an idea of what it does. + Copyright (C) yyyy 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper +mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the +commands you use may be called something other than \`show w' and +\`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This 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](http://www.gnu.org/licenses/lgpl.html) instead of this +License. diff --git a/license.md b/license.md new file mode 100644 index 0000000000..b14d908353 --- /dev/null +++ b/license.md @@ -0,0 +1,503 @@ +### GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public Licenses +are intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + +When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there +is no warranty for the free library. Also, if the library is modified +by someone else and passed on, the recipients should know that what +they have is not the original version, so that the original author's +reputation will not be affected by problems that might be introduced +by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using a +shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). Each +licensee is addressed as "you". + +A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does and +what the program that uses the Library does. + +**1.** You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Library or any +portion of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +- **a)** The modified work must itself be a software library. +- **b)** You must cause the files modified to carry prominent + notices stating that you changed the files and the date of + any change. +- **c)** You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. +- **d)** If a facility in the modified Library refers to a function + or a table of data to be supplied by an application program that + uses the facility, other than as an argument passed when the + facility is invoked, then you must make a good faith effort to + ensure that, in the event an application does not supply such + function or table, the facility still operates, and performs + whatever part of its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of + the application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may opt to apply the terms of the ordinary GNU General +Public License instead of this License to a given copy of the Library. +To do this, you must alter all the notices that refer to this License, +so that they refer to the ordinary GNU General Public License, version +2, instead of to this License. (If a newer version than version 2 of +the ordinary GNU General Public License has appeared, then you can +specify that version instead if you wish.) Do not make any other +change in these notices. + +Once this change is made in a given copy, it is irreversible for that +copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the +Library into a program that is not a library. + +**4.** You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from +a designated place, then offering equivalent access to copy the source +code from the same place satisfies the requirement to distribute the +source code, even though third parties are not compelled to copy the +source along with the object code. + +**5.** A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a work, +in isolation, is not a derivative work of the Library, and therefore +falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. Section +6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure +layouts and accessors, and small macros and small inline functions +(ten lines or less in length), then the use of the object file is +unrestricted, regardless of whether it is legally a derivative work. +(Executables containing this object code plus portions of the Library +will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +**6.** As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a work +containing portions of the Library, and distribute that work under +terms of your choice, provided that the terms permit modification of +the work for the customer's own use and reverse engineering for +debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +- **a)** Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood that + the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) +- **b)** Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (1) uses at run time + a copy of the library already present on the user's computer + system, rather than copying library functions into the executable, + and (2) will operate properly with a modified version of the + library, if the user installs one, as long as the modified version + is interface-compatible with the version that the work was + made with. +- **c)** Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. +- **d)** If distribution of the work is made by offering access to + copy from a designated place, offer equivalent access to copy the + above specified materials from the same place. +- **e)** Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +**7.** 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 not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +- **a)** Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other + library facilities. This must be distributed under the terms of + the Sections above. +- **b)** Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + +**8.** You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +**9.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +**10.** Each time you redistribute the Library (or any work based on +the Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +**11.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Library at all. For +example, if a patent license would not permit royalty-free +redistribution of the Library by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**12.** If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**13.** The Free Software Foundation may publish revised and/or new +versions of the 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 +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +**14.** If you wish to incorporate parts of the Library into other +free programs whose distribution conditions are incompatible with +these, write to the author to ask for permission. For software which +is copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +**NO WARRANTY** + +**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +### END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It +is safest to attach them to the start of each source file to most +effectively convey 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 library's name and an idea of what it does. + Copyright (C) year name of author + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper +mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in + the library `Frob' (a library for tweaking knobs) written + by James Random Hacker. + + signature of Ty Coon, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/server/functional-test/src/test/java/org/zanata/feature/search/comp/GroupSearchCTest.java b/server/functional-test/src/test/java/org/zanata/feature/search/comp/GroupSearchCTest.java index 0ab9041aef..8f537c9713 100644 --- a/server/functional-test/src/test/java/org/zanata/feature/search/comp/GroupSearchCTest.java +++ b/server/functional-test/src/test/java/org/zanata/feature/search/comp/GroupSearchCTest.java @@ -25,7 +25,6 @@ import org.junit.experimental.categories.Category; import org.zanata.feature.testharness.TestPlan; import org.zanata.feature.testharness.ZanataTestCase; -import org.zanata.page.dashboard.DashboardGroupsTab; import org.zanata.page.explore.ExplorePage; import org.zanata.page.groups.VersionGroupPage; import org.zanata.workflow.BasicWorkFlow; @@ -40,8 +39,6 @@ @Category(TestPlan.ComprehensiveTest.class) public class GroupSearchCTest extends ZanataTestCase { - private DashboardGroupsTab dashboardGroupsTab; - @Test(timeout = MAX_SHORT_TEST_DURATION) public void successfulGroupSearchAndDisplay() throws Exception { String groupID = "basic-group"; @@ -49,10 +46,10 @@ public void successfulGroupSearchAndDisplay() throws Exception { assertThat(new LoginWorkFlow().signIn("admin", "admin").loggedInAs()) .isEqualTo("admin") .as("Admin is logged in"); - dashboardGroupsTab = - new BasicWorkFlow().goToHome().goToMyDashboard().gotoGroupsTab(); - VersionGroupPage groupPage = dashboardGroupsTab + new BasicWorkFlow().goToHome() + .goToMyDashboard() + .gotoGroupsTab() .createNewGroup() .inputGroupId(groupID) .inputGroupName(groupName) @@ -80,7 +77,7 @@ public void unsuccessfulGroupSearch() throws Exception { ExplorePage explorePage = new BasicWorkFlow() .goToHome() .gotoExplore() - .enterSearch("group"); + .enterSearch("groop"); assertThat(explorePage.getGroupSearchResults().isEmpty()) .isTrue() diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/UiMessages.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/UiMessages.java index 5384282aa7..d5393a610b 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/UiMessages.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/UiMessages.java @@ -20,8 +20,6 @@ */ package org.zanata.webtrans.client.resources; -import java.util.List; - import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale; import com.google.gwt.i18n.client.LocalizableResource.Generate; import com.google.gwt.i18n.client.Messages; @@ -230,7 +228,7 @@ public interface UiMessages extends Messages { @DefaultMessage("Show as Diff") String diffModeAsDiff(); - @DefaultMessage("Highlight matches") + @DefaultMessage("Highlight Diff") String diffModeAsHighlight(); @DefaultMessage("Translation that contained validation warning or error.") @@ -250,4 +248,10 @@ public interface UiMessages extends Messages { @DefaultMessage("No content") String noContent(); + + @DefaultMessage("Matching") + String matching(); + + @DefaultMessage("Not Matching") + String notMatching(); } diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java index 36a3915fb3..55aab342df 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java @@ -426,7 +426,7 @@ String undoUnsuccessful(@PluralCount int unsuccessfulCount, String noMatch(); @DefaultMessage("Matching") - String noColor(); + String matching(); /** * Used for color legend in search and replace view diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.java index 46b51af25d..0973105c4f 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.java @@ -85,7 +85,7 @@ public void show(ShortcutContext context, DiffMode diffMode) { case TM: searchOnlyLabel.setText(messages.searchOnly()); tmOnlyLabel.setText(messages.tmOnly()); - matchLabel.setText(messages.noColor()); + matchLabel.setText(messages.matching()); searchOnlyDescription.setText(messages.tmInsertTagDesc()); tmOnlyDescription.setText(messages.tmDelTagDesc()); diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.ui.xml b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.ui.xml index 2ab9fccc29..068d2fcff0 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.ui.xml +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/DiffColorLegendPanel.ui.xml @@ -50,9 +50,9 @@ - + - + @@ -63,7 +63,7 @@ - + diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java index 0cbb594b3d..58dde985a2 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/ui/Highlighting.java @@ -120,7 +120,7 @@ private static native String diffsToHtml(JavaScriptObject diffs) html[x] = '' + text + ''; break; case $wnd['DIFF_EQUAL']: - html[x] = '' + text + ''; + html[x] = '' + text + ''; break; } } @@ -150,12 +150,12 @@ private static native String diffsHighlight(JavaScriptObject diffs) '
'); switch (op) { case $wnd['DIFF_INSERT']: - html[x] = text; + html[x] = '' + text + ''; break; case $wnd['DIFF_DELETE']: break; case $wnd['DIFF_EQUAL']: - html[x] = '' + text + ''; + html[x] = text; break; } } diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.java index 33beb8a0aa..13e38ec844 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.java @@ -91,7 +91,7 @@ interface Styles extends CssResource { UiMessages messages; @UiField - InlineLabel searchOnly, tmOnly, diffLegendLabel; + InlineLabel searchOnly, tmOnly, diffLegendLabel, notMatching; @UiField FocusPanel diffLegend; @@ -235,10 +235,14 @@ private void updateDisplayMode() { if (determineDiffMode() == DiffMode.NORMAL) { tmOnly.removeStyleName("is-hidden"); searchOnly.removeStyleName("is-hidden"); + notMatching.removeStyleName("CodeMirror-searching"); + notMatching.setText(messages.matching()); diffLegendLabel.setText(messages.tmDiffHighlighting()); } else { tmOnly.addStyleName("is-hidden"); searchOnly.addStyleName("is-hidden"); + notMatching.addStyleName("CodeMirror-searching"); + notMatching.setText(messages.notMatching()); diffLegendLabel.setText(messages.tmHighlighting()); } } diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.ui.xml b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.ui.xml index 24df9cba3a..0a20cb89ac 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.ui.xml +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/client/view/TransMemoryView.ui.xml @@ -69,7 +69,7 @@
  • - Matching + Not matching TM only Search only diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryQuery.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryQuery.java index 87a0281741..2acad9ac03 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryQuery.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryQuery.java @@ -41,6 +41,7 @@ public class TransMemoryQuery implements IsSerializable, Serializable { private Condition project; private Condition document; private Condition res; + private List fromVersionIds; private Condition includeOwnTranslation = new Condition(true, null); @SuppressWarnings("unused") @@ -67,11 +68,13 @@ public TransMemoryQuery(List queries, SearchType searchType) { } public TransMemoryQuery(List queries, SearchType searchType, - Condition project, Condition document, Condition res) { + Condition project, Condition document, Condition res, + List fromVersionIds) { this(queries, searchType); this.project = project; this.document = document; this.res = res; + this.fromVersionIds = fromVersionIds; } public TransMemoryQuery(String query, SearchType searchType, @@ -110,6 +113,10 @@ public SearchType getSearchType() { return searchType; } + public List getFromVersionIds() { + return fromVersionIds; + } + @Override public String toString() { return "TransMemoryQuery{" + "searchType=" + searchType + ", queries=" diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryResultItem.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryResultItem.java index 8876facc41..b62cef488c 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryResultItem.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/model/TransMemoryResultItem.java @@ -54,6 +54,7 @@ public enum MatchType { // The optional origin identifiers for this result (i.e. A Trans memory name) private List origins; private ArrayList sourceIdList = new ArrayList(); + private Long fromVersionId; // for GWT @SuppressWarnings("unused") @@ -65,14 +66,17 @@ private TransMemoryResultItem() { * @param targetContents * @param relevanceScore * @param similarityPercent + * @param fromVersionId */ public TransMemoryResultItem(ArrayList sourceContents, ArrayList targetContents, MatchType matchType, - double relevanceScore, double similarityPercent) { + double relevanceScore, double similarityPercent, + Long fromVersionId) { super(relevanceScore, similarityPercent); this.sourceContents = sourceContents; this.targetContents = targetContents; this.matchType = matchType; + this.fromVersionId = fromVersionId; this.origins = new ArrayList(); } @@ -134,4 +138,7 @@ public void addSourceId(Long sourceId) { this.sourceIdList.add(sourceId); } + public Long getFromVersionId() { + return fromVersionId; + } } diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/HasTMMergeCriteria.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/HasTMMergeCriteria.java new file mode 100644 index 0000000000..5ba6520d6b --- /dev/null +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/HasTMMergeCriteria.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.webtrans.shared.rest.dto; + +import java.util.List; + +import org.zanata.webtrans.shared.model.ProjectIterationId; +import org.zanata.webtrans.shared.rpc.MergeRule; + +public interface HasTMMergeCriteria { + int getThresholdPercent(); + + MergeRule getDifferentProjectRule(); + + MergeRule getDifferentDocumentRule(); + + MergeRule getDifferentContextRule(); + + MergeRule getImportedMatchRule(); + +} diff --git a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/TransMemoryMergeRequest.java b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/TransMemoryMergeRequest.java index 20e26ef448..155b39dc44 100644 --- a/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/TransMemoryMergeRequest.java +++ b/server/gwt-editor/src/main/java/org/zanata/webtrans/shared/rest/dto/TransMemoryMergeRequest.java @@ -20,6 +20,8 @@ */ package org.zanata.webtrans.shared.rest.dto; +import java.util.List; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.zanata.common.LocaleId; import org.zanata.webtrans.shared.auth.EditorClientId; @@ -30,7 +32,7 @@ /** * @author Patrick Huang pahuang@redhat.com */ -public class TransMemoryMergeRequest { +public class TransMemoryMergeRequest implements HasTMMergeCriteria { @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "For future implement") public EditorClientId editorClientId; @@ -71,23 +73,29 @@ public TransMemoryMergeRequest( public TransMemoryMergeRequest() { } + @Override public int getThresholdPercent() { return thresholdPercent; } + @Override public MergeRule getDifferentProjectRule() { return differentProjectRule; } + @Override public MergeRule getDifferentDocumentRule() { return differentDocumentRule; } + @Override public MergeRule getDifferentContextRule() { return differentContextRule; } + @Override public MergeRule getImportedMatchRule() { return importedMatchRule; } + } diff --git a/server/zanata-frontend/.gitattributes b/server/zanata-frontend/.gitattributes new file mode 100644 index 0000000000..e1898b8d7c --- /dev/null +++ b/server/zanata-frontend/.gitattributes @@ -0,0 +1,3 @@ +*.js text eol=lf +*.jsx text eol=lf +*.jsx.src text eol=lf diff --git a/server/zanata-frontend/src/frontend/app/editor/containers/Root/index.css b/server/zanata-frontend/src/frontend/app/editor/containers/Root/index.css index f8e55c45d8..2b97fe3842 100644 --- a/server/zanata-frontend/src/frontend/app/editor/containers/Root/index.css +++ b/server/zanata-frontend/src/frontend/app/editor/containers/Root/index.css @@ -197,8 +197,9 @@ font-weight: 600; } -li.docName { - max-width: 20em; +li.docName .row { + display: flex; + width: 12rem; } li.docName span.ellipsis { @@ -855,6 +856,11 @@ label span.n1 { max-width: 9.5em; } + li.docName .row { + display: flex; + width: 20rem; + } + #sidebartabs-pane-1 table tbody tr { display: table-row !important; } diff --git a/server/zanata-frontend/src/frontend/app/editor/containers/SuggestionDetailsModal/index.css b/server/zanata-frontend/src/frontend/app/editor/containers/SuggestionDetailsModal/index.css index b0afee3e29..29fa96a183 100644 --- a/server/zanata-frontend/src/frontend/app/editor/containers/SuggestionDetailsModal/index.css +++ b/server/zanata-frontend/src/frontend/app/editor/containers/SuggestionDetailsModal/index.css @@ -8,12 +8,12 @@ .Editor-suggestionsBody .TransUnit--suggestion .TransUnit-text--tight { padding-top: 0 !important; - font-size:1em; + font-size: 1em; } .TransUnit--suggestion .TransUnit-text--tight { - padding-top:40px !important; - font-size:1.2em; + padding-top: 40px !important; + font-size: 1.2em; } /* Based on bootstrap styles. */ @@ -68,7 +68,7 @@ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); box-shadow: 0 1px 1px rgba(0, 0, 0, .05); background-color: #fcfcfc; - line-height:1.2em; + line-height: 1.2em; } .panel-body { @@ -86,7 +86,7 @@ margin-bottom: 0; color: inherit; font-size: 1.143em; - padding-bottom:0 + padding-bottom: 0; } .panel.panel-side { @@ -113,7 +113,7 @@ .panel-info > .panel-heading { border-bottom: solid 1px var(--brand-info); color: var(--brand-info); - background-color:#fcfcfc; + background-color: #fcfcfc; font-weight: 300; } @@ -193,14 +193,14 @@ } .TransUnit-details { - white-space:normal; + white-space: normal; word-break: break-all; - line-height:1em; + line-height: 1em; } .TransUnit-details .u-listInline { - display:inline-flex; - width:100%; + display: block; + width: 100%; } li.TransUnit-label-suggestions { @@ -208,9 +208,9 @@ li.TransUnit-label-suggestions { } span.TransUnit-details-inner { - padding-left:0.375em; - font-weight:500; - color:#444c54; + padding-left: 0.375em; + font-weight: 500; + color: #444c54; } diff --git a/server/zanata-frontend/src/frontend/app/styles/style.less b/server/zanata-frontend/src/frontend/app/styles/style.less index dbd714c617..f4d101b573 100644 --- a/server/zanata-frontend/src/frontend/app/styles/style.less +++ b/server/zanata-frontend/src/frontend/app/styles/style.less @@ -1028,8 +1028,8 @@ pre { display: block; margin: 0 0 @spacing-heading; padding: @spacing-large-horizontal; - word-wrap: break-word; - word-break: break-all; + word-break: normal; + word-break: break-word; /*Chrome specific*/ color: @gray-dark; border: 1px solid #ccc; border-radius: @border-radius-base; @@ -6611,6 +6611,15 @@ table.tmx-table .badge .n1 { } +.translationContainer pre.cm-s-default { + word-wrap: normal; + word-break: normal; + word-break: break-word; /*Chrome specific*/ + -webkit-hyphens: initial; + -moz-hyphens: initial; + hyphens: initial; +} + // @screen-xs variable depreciated @media (max-width: 29.375rem) { .view { diff --git a/server/zanata-model/src/main/java/org/zanata/hibernate/search/IndexFieldLabels.java b/server/zanata-model/src/main/java/org/zanata/hibernate/search/IndexFieldLabels.java index ae8dd9832b..b7d56cdf6a 100644 --- a/server/zanata-model/src/main/java/org/zanata/hibernate/search/IndexFieldLabels.java +++ b/server/zanata-model/src/main/java/org/zanata/hibernate/search/IndexFieldLabels.java @@ -36,4 +36,8 @@ public interface IndexFieldLabels { public static final String GLOSSARY_QUALIFIED_NAME = "glossaryEntry.glossary.qualifiedName"; String PROJECT_VERSION_ID_FIELD = "projectVersionId"; + /* + * Represents the full slug without tokenize + */ + String FULL_SLUG_FIELD = "fullSlug"; } diff --git a/server/zanata-model/src/main/java/org/zanata/model/SlugEntityBase.java b/server/zanata-model/src/main/java/org/zanata/model/SlugEntityBase.java index 6a6459aadd..5fc2e3f783 100644 --- a/server/zanata-model/src/main/java/org/zanata/model/SlugEntityBase.java +++ b/server/zanata-model/src/main/java/org/zanata/model/SlugEntityBase.java @@ -27,8 +27,15 @@ import javax.persistence.Transient; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; + +import org.apache.lucene.analysis.core.SimpleAnalyzer; import org.hibernate.annotations.NaturalId; +import org.hibernate.search.annotations.Analyze; +import org.hibernate.search.annotations.Analyzer; import org.hibernate.search.annotations.Field; +import org.hibernate.search.annotations.Fields; +import org.zanata.hibernate.search.CaseInsensitiveWhitespaceAnalyzer; +import org.zanata.hibernate.search.IndexFieldLabels; import org.zanata.model.validator.Slug; import com.google.common.annotations.VisibleForTesting; @@ -48,7 +55,10 @@ public abstract class SlugEntityBase extends ModelEntityBase { @Size(min = 1, max = 40) @Slug @NotNull - @Field + @Fields({ + @Field, + @Field(analyzer = @Analyzer(impl = CaseInsensitiveWhitespaceAnalyzer.class), name = IndexFieldLabels.FULL_SLUG_FIELD) + }) private String slug; /** diff --git a/server/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java b/server/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java index b77b101dcf..0fd57cecfb 100644 --- a/server/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java +++ b/server/zanata-war/src/main/java/org/zanata/action/MergeTranslationsManager.java @@ -2,7 +2,6 @@ import java.io.Serializable; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import javax.enterprise.context.Dependent; import javax.inject.Inject; @@ -15,6 +14,8 @@ import static org.zanata.async.AsyncTaskKey.joinFields; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + /** * Manages tasks to copy translations from one existing version to another. * @@ -56,7 +57,7 @@ public void start(String sourceProjectSlug, String sourceVersionSlug, AsyncTaskKey key = MergeVersionKey .getKey(targetProjectSlug, targetVersionSlug); - MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle(); + MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle(key); asyncTaskHandleManager.registerTaskHandle(handle, key); mergeTranslationsServiceImpl.startMergeTranslations(sourceProjectSlug, sourceVersionSlug, targetProjectSlug, targetVersionSlug, @@ -106,4 +107,6 @@ public static AsyncTaskKey getKey(String projectSlug, String versionSlug) { } } + + } diff --git a/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java b/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java index a3eb4247cc..9247229109 100644 --- a/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java +++ b/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskHandle.java @@ -51,6 +51,7 @@ public class AsyncTaskHandle implements Serializable { private long finishTime = -1; private String cancelledBy; private long cancelledTime; + private String keyId; public boolean isRunning() { return isStarted() && !isCancelled() && !isDone(); @@ -232,4 +233,12 @@ public long getCancelledTime() { public void setCancelledTime(final long cancelledTime) { this.cancelledTime = cancelledTime; } + + public String getKeyId() { + return keyId; + } + + public void setKeyId(String keyId) { + this.keyId = keyId; + } } diff --git a/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskKey.java b/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskKey.java index 87b2c81792..9a92eb3596 100644 --- a/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskKey.java +++ b/server/zanata-war/src/main/java/org/zanata/async/AsyncTaskKey.java @@ -28,7 +28,7 @@ public interface AsyncTaskKey extends Serializable { * key instance field values * @return String representation of the key which can be used as id */ - static String joinFields(String keyName, String... fields) { + static String joinFields(String keyName, Object... fields) { return keyName + SEPARATOR + Joiner.on(SEPARATOR).useForNull("").join(fields); } diff --git a/server/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java b/server/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java index ddf294455e..585672721e 100644 --- a/server/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java +++ b/server/zanata-war/src/main/java/org/zanata/async/handle/MergeTranslationsTaskHandle.java @@ -21,6 +21,7 @@ package org.zanata.async.handle; import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.AsyncTaskKey; import org.zanata.async.UserTriggeredTaskHandle; /** @@ -31,14 +32,20 @@ public class MergeTranslationsTaskHandle extends AsyncTaskHandle implements UserTriggeredTaskHandle { - private int totalTranslations; + private static final long serialVersionUID = -8026264371441200919L; + private long totalTranslations; private String triggeredBy; - public int getTotalTranslations() { + public MergeTranslationsTaskHandle( + AsyncTaskKey key) { + setKeyId(key.id()); + } + + public long getTotalTranslations() { return this.totalTranslations; } - public void setTotalTranslations(final int totalTranslations) { + public void setTotalTranslations(final long totalTranslations) { this.totalTranslations = totalTranslations; } diff --git a/server/zanata-war/src/main/java/org/zanata/dao/GenericDAO.java b/server/zanata-war/src/main/java/org/zanata/dao/GenericDAO.java index 7043c386d9..74ffe3588a 100644 --- a/server/zanata-war/src/main/java/org/zanata/dao/GenericDAO.java +++ b/server/zanata-war/src/main/java/org/zanata/dao/GenericDAO.java @@ -8,6 +8,8 @@ */ public interface GenericDAO { + int MAX_PARAMS_IN_IN_CLAUSE = 50; + T findById(ID id, boolean lock); void deleteAll(); diff --git a/server/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java b/server/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java index 8186dd3592..df2ea80bea 100644 --- a/server/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java +++ b/server/zanata-war/src/main/java/org/zanata/dao/ProjectDAO.java @@ -50,6 +50,8 @@ import org.zanata.model.HProjectIteration; import org.zanata.model.ProjectRole; +import static org.zanata.hibernate.search.IndexFieldLabels.FULL_SLUG_FIELD; + @RequestScoped public class ProjectDAO extends AbstractDAOImpl { @Inject @FullText @@ -63,6 +65,11 @@ public ProjectDAO(Session session) { super(HProject.class, session); } + public ProjectDAO(FullTextEntityManager entityManager, Session session) { + super(HProject.class, session); + this.entityManager = entityManager; + } + public @Nullable HProject getBySlug(@Nonnull String slug) { if (!StringUtils.isEmpty(slug)) { @@ -280,13 +287,13 @@ public int getQueryProjectSize(@Nonnull String searchQuery, private FullTextQuery buildSearchQuery(@Nonnull String searchQuery, boolean includeObsolete) throws ParseException { - String queryText = QueryParser.escape(searchQuery); - - BooleanQuery booleanQuery = new BooleanQuery(); - booleanQuery.add(buildSearchFieldQuery(queryText, "slug"), BooleanClause.Occur.SHOULD); - booleanQuery.add(buildSearchFieldQuery(queryText, "name"), BooleanClause.Occur.SHOULD); - booleanQuery.add(buildSearchFieldQuery(queryText, "description"), BooleanClause.Occur.SHOULD); + BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); + // for slug, we do prefix search (so people can search with '-' in it + booleanQuery.add(buildSearchFieldQuery(searchQuery, FULL_SLUG_FIELD, true), BooleanClause.Occur.SHOULD); + // for name and description, we split the word using the same analyzer and search as is + booleanQuery.add(buildSearchFieldQuery(searchQuery, "name", false), BooleanClause.Occur.SHOULD); + booleanQuery.add(buildSearchFieldQuery(searchQuery, "description", false), BooleanClause.Occur.SHOULD); if (!includeObsolete) { TermQuery obsoleteStateQuery = new TermQuery(new Term(IndexFieldLabels.ENTITY_STATUS, @@ -294,7 +301,8 @@ private FullTextQuery buildSearchQuery(@Nonnull String searchQuery, booleanQuery.add(obsoleteStateQuery, BooleanClause.Occur.MUST_NOT); } - return entityManager.createFullTextQuery(booleanQuery, HProject.class); + BooleanQuery luceneQuery = booleanQuery.build(); + return entityManager.createFullTextQuery(luceneQuery, HProject.class); } /** @@ -302,24 +310,26 @@ private FullTextQuery buildSearchQuery(@Nonnull String searchQuery, * white space. * * @param searchQuery - * - query string, will replace hypen with space and escape - * special char + * - query string, will escape special char * @param field * - lucene field + * @param wildcard + * - whether append wildcard to the end */ private BooleanQuery buildSearchFieldQuery(@Nonnull String searchQuery, - @Nonnull String field) throws ParseException { - BooleanQuery query = new BooleanQuery(); - - //escape special character search - searchQuery = QueryParser.escape(searchQuery); + @Nonnull String field, boolean wildcard) throws ParseException { + BooleanQuery.Builder query = new BooleanQuery.Builder(); for(String searchString: searchQuery.split("\\s+")) { QueryParser parser = new QueryParser(field, new CaseInsensitiveWhitespaceAnalyzer()); - query.add(parser.parse(searchString + "*"), BooleanClause.Occur.MUST); + //escape special character search + + String escaped = QueryParser.escape(searchString); + escaped = wildcard ? escaped + "*" : escaped; + query.add(parser.parse(escaped), BooleanClause.Occur.MUST); } - return query; + return query.build(); } public List findAllTranslatedProjects(HAccount account, int maxResults) { diff --git a/server/zanata-war/src/main/java/org/zanata/dao/ProjectIterationDAO.java b/server/zanata-war/src/main/java/org/zanata/dao/ProjectIterationDAO.java index 62ef48af14..e5f00f2923 100644 --- a/server/zanata-war/src/main/java/org/zanata/dao/ProjectIterationDAO.java +++ b/server/zanata-war/src/main/java/org/zanata/dao/ProjectIterationDAO.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -50,6 +51,7 @@ import org.zanata.util.HashUtil; import org.zanata.util.StatisticsUtil; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; @RequestScoped @@ -499,23 +501,23 @@ public List searchByProjectId(Long projectId) { return q.list(); } - public List searchByProjectIdExcludingStatus( - Long projectId, EntityStatus... exclude) { - StringBuilder sb = new StringBuilder(); - sb.append("FROM HProjectIteration t WHERE t.project.id = :projectId "); - for (EntityStatus status : exclude) { - sb.append("AND t.status != :"); - sb.append(status.toString()); - } - sb.append(" order by t.creationDate DESC"); - Query q = getSession().createQuery(sb.toString()); - q.setParameter("projectId", projectId); - - for (EntityStatus status : exclude) { - q.setParameter(status.toString(), status); - } - q.setCacheable(false).setComment( - "ProjectIterationDAO.searchByProjectIdExcludeObsolete"); + public List searchByProjectsExcludeObsolete( + List projects) { + Preconditions.checkArgument( + !projects.isEmpty() + && projects.size() < MAX_PARAMS_IN_IN_CLAUSE, + "invalid projectIds list"); + String query = + "FROM HProjectIteration t WHERE t.project.id in (:projectIds) " + + "AND t.status != :status" + + " order by t.project.id, t.creationDate DESC"; + List projectIds = projects.stream().map(HProject::getId).collect( + Collectors.toList()); + Query q = getSession().createQuery(query) + .setParameterList("projectIds", projectIds) + .setParameter("status", EntityStatus.OBSOLETE) + .setCacheable(false).setComment( + "ProjectIterationDAO.searchByProjectIdsExcludeObsolete"); return q.list(); } diff --git a/server/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java b/server/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java index a09ab117fa..e262e1ddbd 100644 --- a/server/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java +++ b/server/zanata-war/src/main/java/org/zanata/dao/TextFlowDAO.java @@ -310,6 +310,41 @@ public int getSourceByMatchedContextCount(Long sourceVersionId, return count == null ? 0 : count.intValue(); } + public long getUntranslatedOrFuzzyTextFlowCountInVersion(Long targetVersionId, HLocale targetLocale) { + String queryString = + "select count(*) from HTextFlow tf left join " + + "tf.targets tfts WITH index(tfts) =:locale" + + " WHERE (tf.obsolete=0 AND tf.document.projectIteration.id=:targetVersionId ) AND" + + " ( EXISTS ( FROM HTextFlowTarget WHERE ((textFlow=tf AND locale.id=:locale) AND state = :untranslatedState)) OR (:locale not in indices(tf.targets) ))"; + Query query = getSession().createQuery(queryString) + .setParameter("locale", targetLocale.getId()) + .setParameter("targetVersionId", targetVersionId) + .setParameter("untranslatedState", ContentState.New) + .setCacheable(true) + .setComment("TextFlowDAO.getUntranslatedOrFuzzyTextFlowCountInVersion"); + Long count = (Long) query.uniqueResult(); + return count == null ? 0 : count; + } + + public List getUntranslatedOrFuzzyTextFlowsInVersion( + Long targetVersionId, + HLocale targetLocale, int startIndex, int maxCount) { + String queryString = + "select distinct tf from HTextFlow tf left join " + + "tf.targets tfts WITH index(tfts) =:locale" + + " WHERE (tf.obsolete=0 AND tf.document.projectIteration.id=:targetVersionId ) AND" + + " ( EXISTS ( FROM HTextFlowTarget WHERE ((textFlow=tf AND locale.id=:locale) AND state = :untranslatedState)) OR (:locale not in indices(tf.targets) ))"; + + Query query = getSession().createQuery(queryString) + .setParameter("locale", targetLocale.getId()) + .setParameter("targetVersionId", targetVersionId) + .setParameter("untranslatedState", ContentState.New) + .setFirstResult(startIndex).setMaxResults(maxCount) + .setCacheable(true) + .setComment("TextFlowDAO.getUntranslatedOrFuzzyTextFlowsInVersion"); + return query.list(); + } + /** * Generate query string for text flows that have matching document id and * content between the given source and target version diff --git a/server/zanata-war/src/main/java/org/zanata/rest/dto/VersionTMMerge.java b/server/zanata-war/src/main/java/org/zanata/rest/dto/VersionTMMerge.java new file mode 100644 index 0000000000..d4a4f8d6ae --- /dev/null +++ b/server/zanata-war/src/main/java/org/zanata/rest/dto/VersionTMMerge.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This 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 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.zanata.rest.dto; + +import java.util.List; + +import org.zanata.common.LocaleId; +import org.zanata.webtrans.shared.model.ProjectIterationId; +import org.zanata.webtrans.shared.rest.dto.HasTMMergeCriteria; +import org.zanata.webtrans.shared.rpc.MergeRule; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +public class VersionTMMerge implements HasTMMergeCriteria { + private LocaleId localeId; + private int thresholdPercent; + private MergeRule differentDocumentRule; + private MergeRule differentContextRule; + private MergeRule importedMatchRule; + private List fromProjectVersions; + + public VersionTMMerge(LocaleId localeId, int thresholdPercent, + MergeRule differentDocumentRule, + MergeRule differentContextRule, + MergeRule importedMatchRule, + List fromProjectVersions) { + this.localeId = localeId; + this.thresholdPercent = thresholdPercent; + this.differentDocumentRule = differentDocumentRule; + this.differentContextRule = differentContextRule; + this.importedMatchRule = importedMatchRule; + this.fromProjectVersions = fromProjectVersions; + } + + @SuppressWarnings("unused") + public VersionTMMerge() { + } + + public LocaleId getLocaleId() { + return localeId; + } + + @Override + public int getThresholdPercent() { + return thresholdPercent; + } + + @Override + public MergeRule getDifferentProjectRule() { + // TM merge for version always accept TM from different project + return MergeRule.FUZZY; + } + + @Override + public MergeRule getDifferentDocumentRule() { + return differentDocumentRule; + } + + @Override + public MergeRule getDifferentContextRule() { + return differentContextRule; + } + + @Override + public MergeRule getImportedMatchRule() { + return importedMatchRule; + } + + public List getFromProjectVersions() { + return fromProjectVersions; + } +} diff --git a/server/zanata-war/src/main/java/org/zanata/rest/editor/service/TransMemoryMergeManager.java b/server/zanata-war/src/main/java/org/zanata/rest/editor/service/TransMemoryMergeManager.java index be516040f9..3336efb8b6 100644 --- a/server/zanata-war/src/main/java/org/zanata/rest/editor/service/TransMemoryMergeManager.java +++ b/server/zanata-war/src/main/java/org/zanata/rest/editor/service/TransMemoryMergeManager.java @@ -20,6 +20,8 @@ */ package org.zanata.rest.editor.service; +import static org.zanata.async.AsyncTaskKey.joinFields; + import java.io.Serializable; import java.util.Objects; @@ -30,21 +32,24 @@ import org.slf4j.LoggerFactory; import org.zanata.async.AsyncTaskHandle; import org.zanata.async.AsyncTaskHandleManager; +import org.zanata.async.AsyncTaskKey; import org.zanata.async.GenericAsyncTaskKey; +import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.async.handle.TransMemoryMergeTaskHandle; import org.zanata.common.LocaleId; -import org.zanata.model.HAccount; -import org.zanata.security.annotations.Authenticated; +import org.zanata.rest.dto.ProcessStatus; +import org.zanata.rest.dto.VersionTMMerge; +import org.zanata.rest.service.AsyncProcessService; +import org.zanata.security.ZanataIdentity; import org.zanata.service.TransMemoryMergeService; import org.zanata.webtrans.shared.model.DocumentId; import org.zanata.webtrans.shared.rest.dto.TransMemoryMergeCancelRequest; import org.zanata.webtrans.shared.rest.dto.TransMemoryMergeRequest; + import com.google.common.base.MoreObjects; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import static org.zanata.async.AsyncTaskKey.joinFields; - /** * @author Patrick Huang * pahuang@redhat.com @@ -54,20 +59,22 @@ public class TransMemoryMergeManager implements Serializable { private static final Logger log = LoggerFactory.getLogger(TransMemoryMergeManager.class); private static final long serialVersionUID = 1364316697376958035L; + private static final String KEY_NAME = "TMMergeForVerKey"; + private final AsyncTaskHandleManager asyncTaskHandleManager; private final TransMemoryMergeService transMemoryMergeService; - private final HAccount authenticated; + private final ZanataIdentity identity; @Inject public TransMemoryMergeManager( AsyncTaskHandleManager asyncTaskHandleManager, TransMemoryMergeService transMemoryMergeService, - @Authenticated HAccount authenticated) { + ZanataIdentity identity) { this.asyncTaskHandleManager = asyncTaskHandleManager; this.transMemoryMergeService = transMemoryMergeService; - this.authenticated = authenticated; + this.identity = identity; } /** @@ -88,7 +95,7 @@ public boolean startTransMemoryMerge(TransMemoryMergeRequest request) { if (handleByKey == null || handleByKey.isCancelled() || handleByKey.isDone()) { TransMemoryMergeTaskHandle handle = new TransMemoryMergeTaskHandle(); - handle.setTriggeredBy(authenticated.getUsername()); + handle.setTriggeredBy(identity.getAccountUsername()); asyncTaskHandleManager.registerTaskHandle(handle, key); transMemoryMergeService.executeMergeAsync(request, handle); return true; @@ -106,19 +113,39 @@ public boolean cancelTransMemoryMerge(TransMemoryMergeCancelRequest request) { TransMemoryMergeTaskHandle handle = (TransMemoryMergeTaskHandle) handleByKey; String triggeredBy = handle.getTriggeredBy(); - if (Objects.equals(authenticated.getUsername(), triggeredBy)) { + if (Objects.equals(identity.getAccountUsername(), triggeredBy)) { handle.cancel(true); handle.setCancelledTime(System.currentTimeMillis()); - handle.setCancelledBy(authenticated.getUsername()); + handle.setCancelledBy(identity.getAccountUsername()); log.info("task: {} cancelled by its creator", handle); return true; } else { - log.warn("{} is attempting to cancel {}", authenticated.getUsername(), handle); + log.warn("{} is attempting to cancel {}", identity.getAccountUsername(), handle); } } return false; } + public AsyncTaskHandle start(Long versionId, VersionTMMerge mergeRequest) { + AsyncTaskKey key = makeKey(versionId, mergeRequest.getLocaleId()); + MergeTranslationsTaskHandle handleByKey = + (MergeTranslationsTaskHandle) asyncTaskHandleManager.getHandleByKey(key); + if (handleByKey == null || handleByKey.isCancelled() + || handleByKey.isDone()) { + handleByKey = new MergeTranslationsTaskHandle(key); + + handleByKey.setTriggeredBy(identity.getAccountUsername()); + asyncTaskHandleManager.registerTaskHandle(handleByKey, key); + transMemoryMergeService.startMergeTranslations(versionId, + mergeRequest, handleByKey); + } else { + log.warn( + "there is already a task running for version id {} and locale {}", + versionId, mergeRequest.getLocaleId()); + } + return handleByKey; + } + @SuppressFBWarnings(value = "EQ_DOESNT_OVERRIDE_EQUALS", justification = "super class equals method is sufficient") static class TMMergeForDocTaskKey extends GenericAsyncTaskKey { @@ -130,7 +157,7 @@ static class TMMergeForDocTaskKey extends TMMergeForDocTaskKey(DocumentId documentId, LocaleId localeId) { // here we use numeric id to form the string id because it doesn't require URL encoding - super(joinFields(KEY_NAME, documentId.getId().toString(), localeId.getId())); + super(joinFields(KEY_NAME, documentId.getId(), localeId)); this.documentId = documentId; this.localeId = localeId; } @@ -143,4 +170,8 @@ public String toString() { .toString(); } } + + private static AsyncTaskKey makeKey(Long versionId, LocaleId localeId) { + return new GenericAsyncTaskKey(joinFields(KEY_NAME, versionId, localeId)); + } } diff --git a/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectSearchResult.java b/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectSearchResult.java index baacf9315f..22e9b70b29 100644 --- a/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectSearchResult.java +++ b/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectSearchResult.java @@ -20,6 +20,8 @@ */ package org.zanata.rest.search.dto; +import java.util.List; + import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.zanata.common.EntityStatus; @@ -35,6 +37,7 @@ public class ProjectSearchResult extends SearchResult { private String title; private long contributorCount; private EntityStatus status; + private List versions; public ProjectSearchResult() { this.setType(SearchResultType.Project); @@ -63,4 +66,12 @@ public EntityStatus getStatus() { public void setStatus(final EntityStatus status) { this.status = status; } + + public List getVersions() { + return versions; + } + + public void setVersions(List versions) { + this.versions = versions; + } } diff --git a/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectVersionSearchResult.java b/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectVersionSearchResult.java new file mode 100644 index 0000000000..2d3a3c609a --- /dev/null +++ b/server/zanata-war/src/main/java/org/zanata/rest/search/dto/ProjectVersionSearchResult.java @@ -0,0 +1,30 @@ +package org.zanata.rest.search.dto; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.zanata.common.EntityStatus; +import org.zanata.rest.dto.SearchResult; + +/** + * @author Patrick Huang pahuang@redhat.com + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +public class ProjectVersionSearchResult extends SearchResult { + private static final long serialVersionUID = 1L; + private EntityStatus status; + + public ProjectVersionSearchResult() { + super.setType(SearchResultType.ProjectVersion); + } + + public ProjectVersionSearchResult(String slug, EntityStatus status) { + super(); + setId(slug); + this.status = status; + } + + public EntityStatus getStatus() { + return status; + } +} diff --git a/server/zanata-war/src/main/java/org/zanata/rest/search/service/SearchService.java b/server/zanata-war/src/main/java/org/zanata/rest/search/service/SearchService.java index 2c56c472ae..01f9652164 100644 --- a/server/zanata-war/src/main/java/org/zanata/rest/search/service/SearchService.java +++ b/server/zanata-war/src/main/java/org/zanata/rest/search/service/SearchService.java @@ -21,6 +21,21 @@ package org.zanata.rest.search.service; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; + import org.apache.commons.lang.StringUtils; import org.apache.deltaspike.jpa.api.transaction.Transactional; import org.apache.lucene.queryparser.classic.ParseException; @@ -28,32 +43,26 @@ import org.zanata.dao.LocaleDAO; import org.zanata.dao.PersonDAO; import org.zanata.dao.ProjectDAO; +import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.VersionGroupDAO; import org.zanata.model.HAccount; import org.zanata.model.HIterationGroup; import org.zanata.model.HProject; +import org.zanata.model.HProjectIteration; import org.zanata.rest.dto.SearchResult; import org.zanata.rest.search.dto.GroupSearchResult; import org.zanata.rest.search.dto.PersonSearchResult; import org.zanata.rest.search.dto.ProjectSearchResult; +import org.zanata.rest.search.dto.ProjectVersionSearchResult; import org.zanata.rest.search.dto.SearchResults; +import org.zanata.rest.service.RestResource; import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; import org.zanata.service.GravatarService; import org.zanata.service.LocaleService; -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; -import java.util.List; -import java.util.stream.Collectors; - -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; /** * @author Carlos Munoz camunoz@redhat.com @@ -62,11 +71,14 @@ @Path("/search") @Produces(APPLICATION_JSON) @Transactional(readOnly = true) -public class SearchService { +public class SearchService implements RestResource { @Inject private ProjectDAO projectDAO; + @Inject + private ProjectIterationDAO projectIterationDAO; + @Inject private PersonDAO personDAO; @@ -92,7 +104,8 @@ public class SearchService { public Response searchProjects( @QueryParam("q") @DefaultValue("") String query, @DefaultValue("1") @QueryParam("page") int page, - @DefaultValue("20") @QueryParam("sizePerPage") int sizePerPage) { + @DefaultValue("20") @QueryParam("sizePerPage") int sizePerPage, + @DefaultValue("false") @QueryParam("includeVersion") boolean includeVersion) { int offset = (validatePage(page) - 1) * validatePageSize(sizePerPage); @@ -110,12 +123,36 @@ public Response searchProjects( validatePageSize(sizePerPage), offset, false); } + + Map> projectSlugToVersions = + Maps.newHashMap(); + if (includeVersion && !projects.isEmpty()) { + List versions = projectIterationDAO + .searchByProjectsExcludeObsolete(projects); + versions.forEach(ver -> { + String projectSlug = ver.getProject().getSlug(); + List iterations = projectSlugToVersions + .getOrDefault(projectSlug, + Lists.newLinkedList()); + iterations.add(ver); + projectSlugToVersions.put(projectSlug, iterations); + }); + } List results = projects.stream().map(p -> { ProjectSearchResult result = new ProjectSearchResult(); result.setId(p.getSlug()); result.setStatus(p.getStatus()); result.setTitle(p.getName()); result.setDescription(p.getDescription()); + if (includeVersion) { + List iterations = + projectSlugToVersions.get(p.getSlug()); + result.setVersions(iterations == null ? null : iterations + .stream() + .map(iteration -> new ProjectVersionSearchResult( + iteration.getSlug(), iteration.getStatus())) + .collect(Collectors.toList())); + } // TODO: include contributor count when data is available return result; }).collect(Collectors.toList()); diff --git a/server/zanata-war/src/main/java/org/zanata/rest/service/AsyncProcessService.java b/server/zanata-war/src/main/java/org/zanata/rest/service/AsyncProcessService.java index 10c56a97b8..0bfc24c5e8 100644 --- a/server/zanata-war/src/main/java/org/zanata/rest/service/AsyncProcessService.java +++ b/server/zanata-war/src/main/java/org/zanata/rest/service/AsyncProcessService.java @@ -206,7 +206,7 @@ public Response cancelAsyncProcess(@PathParam("keyId") String keyId) { } @NotNull - static ProcessStatus handleToProcessStatus(AsyncTaskHandle handle, + public static ProcessStatus handleToProcessStatus(AsyncTaskHandle handle, String url) { ProcessStatus status = new ProcessStatus(); status.setStatusCode( diff --git a/server/zanata-war/src/main/java/org/zanata/rest/service/ProjectVersionService.java b/server/zanata-war/src/main/java/org/zanata/rest/service/ProjectVersionService.java index 493440991f..b547d3ea04 100644 --- a/server/zanata-war/src/main/java/org/zanata/rest/service/ProjectVersionService.java +++ b/server/zanata-war/src/main/java/org/zanata/rest/service/ProjectVersionService.java @@ -4,26 +4,34 @@ import static org.zanata.common.EntityStatus.OBSOLETE; import static org.zanata.common.EntityStatus.READONLY; import static org.zanata.webtrans.server.rpc.GetTransUnitsNavigationService.TextFlowResultTransformer; + import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; + import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.DefaultValue; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import com.google.common.base.Objects; + import org.apache.commons.lang.StringUtils; import org.apache.deltaspike.jpa.api.transaction.Transactional; import org.zanata.ApplicationConfiguration; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.common.ContentState; import org.zanata.common.EntityStatus; +import org.zanata.common.LocaleId; import org.zanata.common.ProjectType; import org.zanata.dao.DocumentDAO; import org.zanata.dao.ProjectDAO; @@ -38,17 +46,22 @@ import org.zanata.rest.NoSuchEntityException; import org.zanata.rest.ReadOnlyEntityException; import org.zanata.rest.dto.LocaleDetails; +import org.zanata.rest.dto.ProcessStatus; import org.zanata.rest.dto.ProjectIteration; import org.zanata.rest.dto.TransUnitStatus; import org.zanata.rest.dto.User; +import org.zanata.rest.dto.VersionTMMerge; import org.zanata.rest.dto.resource.ResourceMeta; +import org.zanata.rest.editor.service.TransMemoryMergeManager; import org.zanata.rest.editor.service.UserService; -import org.zanata.webtrans.shared.search.FilterConstraints; import org.zanata.security.ZanataIdentity; import org.zanata.service.ConfigurationService; import org.zanata.service.LocaleService; import org.zanata.webtrans.shared.model.DocumentId; +import org.zanata.webtrans.shared.search.FilterConstraints; + import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; import com.google.common.collect.Lists; /** @@ -86,6 +99,8 @@ public class ProjectVersionService implements ProjectVersionResource { private ApplicationConfiguration applicationConfiguration; @Context private UriInfo uri; + @Inject + private TransMemoryMergeManager transMemoryMergeManager; @Override public Response head(@PathParam("projectSlug") String projectSlug, @@ -112,21 +127,13 @@ public Response put(@PathParam("projectSlug") String projectSlug, return projTypeError; } HProject hProject = projectDAO.getBySlug(projectSlug); - if (hProject == null) { - return Response.status(Response.Status.NOT_FOUND) - .entity("Project \'" + projectSlug + "\' not found.") - .build(); - } else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { - // Project is Obsolete - return Response.status(Response.Status.NOT_FOUND) - .entity("Project \'" + projectSlug + "\' not found.") - .build(); - } else if (Objects.equal(hProject.getStatus(), READONLY)) { - // Project is ReadOnly - return Response.status(Response.Status.FORBIDDEN) - .entity("Project \'" + projectSlug + "\' is read-only.") - .build(); + + Optional projectResponse = + getResponseIfProjectIsNotActive(hProject, projectSlug); + if (projectResponse.isPresent()) { + return projectResponse.get(); } + HProjectIteration hProjectVersion = projectIterationDAO.getBySlug(projectSlug, versionSlug); if (hProjectVersion == null) { @@ -179,6 +186,26 @@ public Response put(@PathParam("projectSlug") String projectSlug, return response.tag(etag).build(); } + private Optional getResponseIfProjectIsNotActive( + HProject hProject, String projectSlug) { + if (hProject == null) { + return Optional.of(Response.status(Response.Status.NOT_FOUND) + .entity("Project \'" + projectSlug + "\' not found.") + .build()); + } else if (Objects.equal(hProject.getStatus(), OBSOLETE)) { + // Project is Obsolete + return Optional.of(Response.status(Response.Status.NOT_FOUND) + .entity("Project \'" + projectSlug + "\' not found.") + .build()); + } else if (Objects.equal(hProject.getStatus(), READONLY)) { + // Project is ReadOnly + return Optional.of(Response.status(Response.Status.FORBIDDEN) + .entity("Project \'" + projectSlug + "\' is read-only.") + .build()); + } + return Optional.empty(); + } + @Override public Response sampleConfiguration( @PathParam("projectSlug") String projectSlug, @@ -308,6 +335,82 @@ public Response getTransUnitStatus( return Response.ok(entity).build(); } + /** + * + * Trigger a TM merge for the target version. It will run in the background + * and pre-fill translations from TM based on user selected criteria. + * + * @param projectSlug + * The project slug/ID + * @param versionSlug + * The project version slug/ID + * @param mergeRequest + * TM merge criteria + * @return The following response status codes will be returned from this + * operation:
    + * ACCEPTED(202) - If the process is successfully triggered.
    + * UNACCEPTED(400) - If the incoming payload is invalid.
    + * NOT FOUND(404) - If no project or version was found for the given + * project slug and version slug.
    + * FORBIDDEN(403) - If the user was not allowed to create/modify the + * project iteration. In this case an error message is contained in + * the response.
    + * UNAUTHORIZED(401) - If the user does not have the proper + * permissions to perform this operation.
    + * INTERNAL SERVER ERROR(500) - If there is an unexpected error in + * the server while performing this operation. + */ + @POST + @Path(VERSION_SLUG_TEMPLATE + "/tm-merge") + public Response prefillWithTM(@PathParam("projectSlug") String projectSlug, + @PathParam("versionSlug") String versionSlug, + VersionTMMerge mergeRequest) { + int percent = mergeRequest.getThresholdPercent(); + if (percent < 80 || percent > 100) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("{\"error\":\"percentThreshold must be between 80 and 100\"}") + .build(); + } + HProject hProject = projectDAO.getBySlug(projectSlug); + + Optional projectResponse = + getResponseIfProjectIsNotActive(hProject, projectSlug); + if (projectResponse.isPresent()) { + return projectResponse.get(); + } + HProjectIteration version = + projectIterationDAO.getBySlug(hProject, versionSlug); + if (version == null || version.getStatus() == OBSOLETE) { + return Response.status(Response.Status.NOT_FOUND) + .entity("Project version \'" + projectSlug + ":" + + versionSlug + "\' not found.") + .build(); + } else if (version.getStatus() == READONLY) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Project version \'" + projectSlug + ":" + + versionSlug + "\' is readonly.") + .build(); + } else if (version.getDocuments().isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("{\"error\":\"project version has no documents\"}") + .build(); + } + LocaleId localeId = mergeRequest.getLocaleId(); + HLocale hLocale = localeServiceImpl.getByLocaleId(localeId); + if (hLocale == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + identity.checkPermission("modify-translation", hProject, hLocale); + AsyncTaskHandle handle = transMemoryMergeManager + .start(version.getId(), mergeRequest); + + ProcessStatus processStatus = AsyncProcessService + .handleToProcessStatus(handle, + uri.getBaseUri() + "process/key/" + handle.getKeyId()); + return Response.accepted(processStatus).build(); + } + @VisibleForTesting protected HProjectIteration retrieveAndCheckIteration(String projectSlug, String versionSlug, boolean writeOperation) { diff --git a/server/zanata-war/src/main/java/org/zanata/rest/service/package-info.java b/server/zanata-war/src/main/java/org/zanata/rest/service/package-info.java deleted file mode 100644 index 7771b124ab..0000000000 --- a/server/zanata-war/src/main/java/org/zanata/rest/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package org.zanata.rest.service; diff --git a/server/zanata-war/src/main/java/org/zanata/service/LocaleService.java b/server/zanata-war/src/main/java/org/zanata/service/LocaleService.java index d0cb1f9c52..bd0e8595f8 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/LocaleService.java +++ b/server/zanata-war/src/main/java/org/zanata/service/LocaleService.java @@ -33,6 +33,8 @@ import org.zanata.common.LocaleId; import org.zanata.exception.ZanataServiceException; import org.zanata.model.HLocale; +import org.zanata.model.HProject; +import org.zanata.model.HProjectIteration; import org.zanata.model.HLocaleMember; import org.zanata.model.HTextFlowTarget; import org.zanata.rest.dto.LanguageTeamSearchResult; @@ -92,12 +94,18 @@ HLocale validateLocaleByProject(@Nonnull LocaleId locale, // TODO I don't think this method is specifically about source languages HLocale validateSourceLocale(LocaleId locale) throws ZanataServiceException; + List + getSupportedLanguageByProject(@Nonnull HProject project); + List getTranslation(@Nonnull String project, @Nonnull String iterationSlug, String username); List getSupportedLanguageByProjectIteration( @Nonnull String projectSlug, @Nonnull String iterationSlug); + List getSupportedLanguageByProjectIteration( + @Nonnull HProjectIteration version); + List getSupportedLanguageByProject(@Nonnull String project); Map getGlobalLocaleItems(); diff --git a/server/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java b/server/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java index d91d96f821..368537b1b2 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java +++ b/server/zanata-war/src/main/java/org/zanata/service/MergeTranslationsService.java @@ -52,4 +52,5 @@ Future startMergeTranslations(String sourceProjectSlug, */ int getTotalProgressCount(HProjectIteration sourceVersion, HProjectIteration targetVersion); + } diff --git a/server/zanata-war/src/main/java/org/zanata/service/TransMemoryMergeService.java b/server/zanata-war/src/main/java/org/zanata/service/TransMemoryMergeService.java index 71741972cd..3119db01ee 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/TransMemoryMergeService.java +++ b/server/zanata-war/src/main/java/org/zanata/service/TransMemoryMergeService.java @@ -24,7 +24,14 @@ import java.util.List; import java.util.concurrent.Future; +import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.async.handle.TransMemoryMergeTaskHandle; +import org.zanata.model.HLocale; +import org.zanata.model.HProjectIteration; +import org.zanata.model.HTextFlow; +import org.zanata.rest.dto.VersionTMMerge; +import org.zanata.webtrans.shared.model.ProjectIterationId; +import org.zanata.webtrans.shared.rest.dto.HasTMMergeCriteria; import org.zanata.webtrans.shared.rest.dto.TransMemoryMergeRequest; public interface TransMemoryMergeService extends Serializable { @@ -33,6 +40,23 @@ List executeMerge( TransMemoryMergeRequest request, TransMemoryMergeTaskHandle asyncTaskHandle); + /** + * TM merge for a single document + * @param request + * @param asyncTaskHandle + * @return + */ Future> executeMergeAsync(TransMemoryMergeRequest request, TransMemoryMergeTaskHandle asyncTaskHandle); + + /** + * TM merge for a project version + * @param versionId + * @param mergeRequest + * @param handle + * @return + */ + Future startMergeTranslations(Long versionId, + VersionTMMerge mergeRequest, + MergeTranslationsTaskHandle handle); } diff --git a/server/zanata-war/src/main/java/org/zanata/service/TranslationMemoryService.java b/server/zanata-war/src/main/java/org/zanata/service/TranslationMemoryService.java index 02f421e2c2..ace0d38e5a 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/TranslationMemoryService.java +++ b/server/zanata-war/src/main/java/org/zanata/service/TranslationMemoryService.java @@ -52,12 +52,14 @@ Optional searchBestMatchTransMemory(HTextFlow textFlow, * @param checkDocument * @param checkProject * @param thresholdPercent + * @param fromVersionIds * @return */ Optional searchBestMatchTransMemory( HTextFlow textFlow, LocaleId targetLocaleId, LocaleId sourceLocaleId, boolean checkContext, - boolean checkDocument, boolean checkProject, int thresholdPercent); + boolean checkDocument, boolean checkProject, int thresholdPercent, + List fromVersionIds); List searchTransMemory(LocaleId targetLocaleId, LocaleId sourceLocaleId, TransMemoryQuery transMemoryQuery); diff --git a/server/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java b/server/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java index c84ff9fd75..853a92e53e 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java +++ b/server/zanata-war/src/main/java/org/zanata/service/impl/LocaleServiceImpl.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; + import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.enterprise.context.ApplicationScoped; @@ -53,6 +54,7 @@ import org.zanata.service.LocaleService; import org.zanata.servlet.annotations.AllJavaLocales; import org.zanata.util.ComparatorUtil; + import com.google.common.collect.Maps; import com.ibm.icu.util.ULocale; @@ -318,6 +320,15 @@ public List getSupportedLanguageByProjectIteration( return getSupportedLanguageByProject(projectSlug); } + @Override + public List getSupportedLanguageByProjectIteration( + @Nonnull HProjectIteration version) { + if (version.isOverrideLocales()) { + return new ArrayList<>(version.getCustomizedLocales()); + } + return getSupportedLanguageByProject(version.getProject()); + } + @Override public List getSupportedLanguageByProject(@Nonnull String projectSlug) { @@ -328,6 +339,15 @@ public List getSupportedLanguageByProjectIteration( return localeDAO.findAllActiveAndEnabledByDefault(); } + @Override + public List + getSupportedLanguageByProject(@Nonnull HProject project) { + if (project.isOverrideLocales()) { + return new ArrayList<>(project.getCustomizedLocales()); + } + return localeDAO.findAllActiveAndEnabledByDefault(); + } + @Override public List getTranslation(@Nonnull String project, @Nonnull String iterationSlug, String username) { diff --git a/server/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java b/server/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java index 9d4cbd796a..83fcddb65d 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java +++ b/server/zanata-war/src/main/java/org/zanata/service/impl/MergeTranslationsServiceImpl.java @@ -20,22 +20,26 @@ */ package org.zanata.service.impl; +import static org.zanata.events.TextFlowTargetStateEvent.TextFlowTargetStateChange; +import static org.zanata.transaction.TransactionUtilImpl.runInTransaction; + import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Future; +import java.util.stream.Collectors; + import javax.annotation.Nonnull; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Multimap; import javax.enterprise.context.RequestScoped; import javax.enterprise.event.Event; import javax.inject.Inject; import javax.inject.Named; + import org.zanata.async.Async; import org.zanata.async.AsyncTaskResult; import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.common.ContentState; +import org.zanata.common.EntityStatus; import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowDAO; import org.zanata.events.DocStatsEvent; @@ -47,19 +51,24 @@ import org.zanata.model.HSimpleComment; import org.zanata.model.HTextFlow; import org.zanata.model.HTextFlowTarget; +import org.zanata.model.ModelEntityBase; import org.zanata.model.type.TranslationSourceType; +import org.zanata.rest.dto.VersionTMMerge; import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; import org.zanata.service.LocaleService; import org.zanata.service.MergeTranslationsService; +import org.zanata.service.TransMemoryMergeService; import org.zanata.service.TranslationStateCache; import org.zanata.service.VersionStateCache; import org.zanata.util.TranslationUtil; + import com.google.common.base.Optional; import com.google.common.base.Stopwatch; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; -import static org.zanata.events.TextFlowTargetStateEvent.TextFlowTargetStateChange; -import static org.zanata.transaction.TransactionUtilImpl.runInTransaction; +import com.google.common.collect.Multimap; // Not @Transactional, because we use runInTransaction /** @@ -80,8 +89,6 @@ public class MergeTranslationsServiceImpl implements MergeTranslationsService { @Inject private TextFlowDAO textFlowDAO; @Inject - private ZanataIdentity identity; - @Inject private VersionStateCache versionStateCacheImpl; @Inject private TranslationStateCache translationStateCacheImpl; @@ -302,12 +309,7 @@ private void mergeTextFlowTarget(HTextFlowTarget sourceTft, */ private boolean isVersionsEmpty(HProjectIteration sourceVersion, HProjectIteration targetVersion) { - if (sourceVersion.getDocuments().isEmpty()) { - log.error("No documents in source version {}:{}", - sourceVersion.getProject().getSlug(), - sourceVersion.getSlug()); - return true; - } + if (isVersionEmpty(sourceVersion)) return true; if (targetVersion.getDocuments().isEmpty()) { log.error("No documents in target version {}:{}", targetVersion.getProject().getSlug(), @@ -317,11 +319,21 @@ private boolean isVersionsEmpty(HProjectIteration sourceVersion, return false; } + private boolean isVersionEmpty(HProjectIteration version) { + if (version.getDocuments().isEmpty()) { + log.error("No documents in version {}:{}", + version.getProject().getSlug(), + version.getSlug()); + return true; + } + return false; + } + private void prepareMergeTranslationsHandle( @Nonnull HProjectIteration sourceVersion, @Nonnull HProjectIteration targetVersion, @Nonnull MergeTranslationsTaskHandle handle) { - handle.setTriggeredBy(identity.getAccountUsername()); + handle.setTriggeredBy(authenticatedAccount.getUsername()); int total = getTotalProgressCount(sourceVersion, targetVersion); handle.setMaxProgress(total); handle.setTotalTranslations(total); @@ -390,4 +402,5 @@ public static boolean shouldMerge(HTextFlowTarget sourceTft, return useNewerTranslation && sourceTft.getLastChanged().after(targetTft.getLastChanged()); } + } diff --git a/server/zanata-war/src/main/java/org/zanata/service/impl/TransMemoryMergeServiceImpl.java b/server/zanata-war/src/main/java/org/zanata/service/impl/TransMemoryMergeServiceImpl.java index 10bc5a2094..3f90267ba5 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/impl/TransMemoryMergeServiceImpl.java +++ b/server/zanata-war/src/main/java/org/zanata/service/impl/TransMemoryMergeServiceImpl.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.enterprise.event.Event; @@ -36,8 +38,11 @@ import org.slf4j.LoggerFactory; import org.zanata.async.Async; import org.zanata.async.AsyncTaskResult; +import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.async.handle.TransMemoryMergeTaskHandle; import org.zanata.common.ContentState; +import org.zanata.common.EntityStatus; +import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowDAO; import org.zanata.dao.TransMemoryUnitDAO; import org.zanata.events.TextFlowTargetUpdateContextEvent; @@ -45,16 +50,21 @@ import org.zanata.events.TransMemoryMergeProgressEvent; import org.zanata.model.HAccount; import org.zanata.model.HLocale; +import org.zanata.model.HProjectIteration; import org.zanata.model.HTextFlow; import org.zanata.model.HTextFlowTarget; +import org.zanata.model.ModelEntityBase; import org.zanata.model.tm.TransMemoryUnit; import org.zanata.model.type.EntityType; import org.zanata.model.type.TranslationSourceType; +import org.zanata.rest.dto.VersionTMMerge; +import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; import org.zanata.service.LocaleService; import org.zanata.service.TransMemoryMergeService; import org.zanata.service.TranslationMemoryService; import org.zanata.service.TranslationService; +import org.zanata.service.VersionStateCache; import org.zanata.transaction.TransactionUtil; import org.zanata.util.TransMemoryMergeStatusResolver; import org.zanata.util.TranslationUtil; @@ -63,17 +73,20 @@ import org.zanata.webtrans.shared.model.TransUnitId; import org.zanata.webtrans.shared.model.TransUnitUpdateRequest; import org.zanata.webtrans.shared.model.WorkspaceId; +import org.zanata.webtrans.shared.rest.dto.HasTMMergeCriteria; import org.zanata.webtrans.shared.rest.dto.TransMemoryMergeRequest; import org.zanata.webtrans.shared.rpc.MergeRule; import org.zanata.webtrans.shared.rpc.TransUnitUpdated; import org.zanata.webtrans.shared.search.FilterConstraints; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; /** * @author Sean Flanigan * sflaniga@redhat.com + * @author Patrick Huang pahuang@redhat.com */ @Named("transMemoryMergeServiceImpl") @RequestScoped @@ -110,6 +123,15 @@ public class TransMemoryMergeServiceImpl implements TransMemoryMergeService { @Inject private TransactionUtil transactionUtil; + @Inject + private ProjectIterationDAO projectIterationDAO; + + @Inject + private ZanataIdentity identity; + + @Inject + private VersionStateCache versionStateCacheImpl; + @Inject @Authenticated private HAccount authenticatedAccount; @@ -157,8 +179,17 @@ public List executeMerge( index = index + processedSize; asyncTaskHandle.increaseProgress(processedSize); + Consumer + callback = + updateRequest -> textFlowTargetUpdateContextEvent + .fire(new TextFlowTargetUpdateContextEvent( + updateRequest.getTransUnitId(), + request.localeId, + request.editorClientId, + TransUnitUpdated.UpdateType.NonEditorSave)); List batchResult = - translateInBatch(request, textFlowsBatch, targetLocale); + translateInBatch(request, textFlowsBatch, targetLocale, + Collections.emptyList(), Optional.of(callback)); finalResult.addAll(batchResult); log.debug("TM merge handle: {}", asyncTaskHandle); transMemoryMergeProgressEvent @@ -190,20 +221,95 @@ public Future> executeMergeAsync(Tran return AsyncTaskResult.completed(translationResults); } + @Async + @Override + public Future startMergeTranslations(Long targetVersionId, + VersionTMMerge mergeRequest, + MergeTranslationsTaskHandle handle) { + // since this is async we need to reload entities + HProjectIteration targetVersion = + projectIterationDAO.findById(targetVersionId); + HLocale targetLocale = + localeServiceImpl.getByLocaleId(mergeRequest.getLocaleId()); + + List localesInTargetVersion = localeServiceImpl + .getSupportedLanguageByProjectIteration(targetVersion); + if (!localesInTargetVersion.contains(targetLocale)) { + log.error("No locales enabled in target version of [{}]", + targetVersion.userFriendlyToString()); + return AsyncTaskResult.completed(); + } + + List fromVersionIds = mergeRequest.getFromProjectVersions().stream() + .map(projectIterationId -> projectIterationDAO.getBySlug( + projectIterationId.getProjectSlug(), + projectIterationId.getIterationSlug())) + .filter(ver -> ver != null + && ver.getStatus() != EntityStatus.OBSOLETE + && localeServiceImpl + .getSupportedLanguageByProjectIteration(ver) + .contains(targetLocale)) + .map(ModelEntityBase::getId).collect(Collectors.toList()); + + + long mergeTargetCount = textFlowDAO.getUntranslatedOrFuzzyTextFlowCountInVersion( + targetVersion.getId(), targetLocale); + + Optional + taskHandleOpt = Optional.ofNullable(handle); + if (taskHandleOpt.isPresent()) { + MergeTranslationsTaskHandle handle1 = taskHandleOpt.get(); + handle1.setTriggeredBy(identity.getAccountUsername()); + handle1.setMaxProgress((int) mergeTargetCount); + handle1.setTotalTranslations(mergeTargetCount); + } + Stopwatch overallStopwatch = Stopwatch.createStarted(); + log.info("merge translations from TM start: from {} to {}", + fromVersionIds, + targetVersion.userFriendlyToString()); + int startCount = 0; + + while (startCount < mergeTargetCount) { + List batch = textFlowDAO + .getUntranslatedOrFuzzyTextFlowsInVersion( + targetVersion.getId(), targetLocale, startCount, + BATCH_SIZE); + translateInBatch(mergeRequest, batch, + targetLocale, fromVersionIds, Optional.empty()); + + taskHandleOpt.ifPresent( + mergeTranslationsTaskHandle -> mergeTranslationsTaskHandle + .increaseProgress(batch.size())); + startCount += BATCH_SIZE; + } + versionStateCacheImpl.clearVersionStatsCache(targetVersion.getId()); + log.info("merge translation from TM end: from {} to {}, {}", + mergeRequest.getFromProjectVersions(), + targetVersion.userFriendlyToString(), overallStopwatch); + return AsyncTaskResult.completed(); + } + /** * This method will run in transaction and manages its own transaction. * * @param request - * TM merge request + * TM merge request criteria * @param textFlows * the text flows to be filled * @param targetLocale * target locale + * @param fromVersionIds + * source version ids + * @param callbackOnUpdate + * an optional callback to call when we have a + * TransUnitUpdateRequest ready * @return translation results */ private List translateInBatch( - TransMemoryMergeRequest request, List textFlows, - HLocale targetLocale) { + HasTMMergeCriteria request, List textFlows, + HLocale targetLocale, + List fromVersionIds, + Optional> callbackOnUpdate) { if (textFlows.isEmpty()) { return Collections.emptyList(); @@ -226,7 +332,8 @@ private List translateInBatch( hTextFlow, targetLocale.getLocaleId(), hTextFlow.getDocument().getLocale().getLocaleId(), checkContext, checkDocument, checkProject, - request.getThresholdPercent()); + request.getThresholdPercent(), + fromVersionIds); if (tmResult.isPresent()) { TransUnitUpdateRequest updateRequest = createRequest(request, targetLocale, @@ -234,17 +341,12 @@ private List translateInBatch( if (updateRequest != null) { updateRequests.add(updateRequest); - textFlowTargetUpdateContextEvent - .fire(new TextFlowTargetUpdateContextEvent( - updateRequest.getTransUnitId(), - request.localeId, - request.editorClientId, - TransUnitUpdated.UpdateType.NonEditorSave)); + callbackOnUpdate.ifPresent(c -> c.accept(updateRequest)); } } } return translationServiceImpl.translate( - request.localeId, updateRequests); + targetLocale.getLocaleId(), updateRequests); }); } catch (Exception e) { log.error("exception during TM merge", e); @@ -252,7 +354,7 @@ private List translateInBatch( } } - private TransUnitUpdateRequest createRequest(TransMemoryMergeRequest action, + private TransUnitUpdateRequest createRequest(HasTMMergeCriteria action, HLocale hLocale, HTextFlow hTextFlowToBeFilled, TransMemoryResultItem tmResult, HTextFlowTarget oldTarget) { diff --git a/server/zanata-war/src/main/java/org/zanata/service/impl/TranslationMemoryServiceImpl.java b/server/zanata-war/src/main/java/org/zanata/service/impl/TranslationMemoryServiceImpl.java index 4ee741a477..3119974a0b 100644 --- a/server/zanata-war/src/main/java/org/zanata/service/impl/TranslationMemoryServiceImpl.java +++ b/server/zanata-war/src/main/java/org/zanata/service/impl/TranslationMemoryServiceImpl.java @@ -21,6 +21,24 @@ package org.zanata.service.impl; import static com.google.common.collect.Collections2.filter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; + import org.apache.commons.lang.StringUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; @@ -36,9 +54,6 @@ import org.apache.lucene.search.WildcardQuery; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.inject.Named; import org.zanata.common.ContentState; import org.zanata.common.EntityStatus; import org.zanata.common.LocaleId; @@ -67,21 +82,12 @@ import org.zanata.webtrans.shared.model.TransMemoryResultItem; import org.zanata.webtrans.shared.rpc.HasSearchType; import org.zanata.webtrans.shared.rpc.LuceneQuery; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import javax.annotation.Nonnull; /** * @author Alex Eng aeng@redhat.com @@ -155,6 +161,7 @@ public TransMemoryDetails getTransMemoryDetail(HLocale hLocale, } /** + * TODO this is only used by test. Should we remove it? * This is used by CopyTrans, with ContentHash search in lucene. Returns * first entry of the matches which sort by HTextFlowTarget.lastChanged DESC * @@ -172,7 +179,8 @@ public Optional searchBestMatchTransMemory( boolean checkDocument, boolean checkProject) { TransMemoryQuery query = buildTMQuery(textFlow, HasSearchType.SearchType.CONTENT_HASH, - checkContext, checkDocument, checkProject, false); + checkContext, checkDocument, checkProject, false, + Collections.emptyList()); Collection matches = findMatchingTranslation(targetLocaleId, sourceLocaleId, query, 0, Optional.empty(), HTextFlowTarget.class); @@ -185,7 +193,6 @@ public Optional searchBestMatchTransMemory( /** * This is used by TMMerge. Returns first entry of the matches which sort by * similarityPercent, sourceContents, and contents size. - * * @param textFlow * @param targetLocaleId * @param sourceLocaleId @@ -193,15 +200,18 @@ public Optional searchBestMatchTransMemory( * @param checkDocument * @param checkProject * @param thresholdPercent + * @param fromVersionIds */ @Override public Optional searchBestMatchTransMemory( HTextFlow textFlow, LocaleId targetLocaleId, LocaleId sourceLocaleId, boolean checkContext, - boolean checkDocument, boolean checkProject, int thresholdPercent) { + boolean checkDocument, boolean checkProject, int thresholdPercent, + List fromVersionIds) { TransMemoryQuery query = buildTMQuery(textFlow, HasSearchType.SearchType.FUZZY_PLURAL, - checkContext, checkDocument, checkProject, true); + checkContext, checkDocument, checkProject, + true, fromVersionIds); List tmResults = searchTransMemory(targetLocaleId, sourceLocaleId, query); // findTMAboveThreshold @@ -232,7 +242,7 @@ public List searchTransMemory( } List results = Lists.newArrayList(matchesMap.values()); - Collections.sort(results, TransMemoryResultComparator.COMPARATOR); + Collections.sort(results, new TransMemoryResultComparator(transMemoryQuery.getFromVersionIds())); return results; } @@ -248,7 +258,8 @@ public List searchTransMemoryWithDetails( private TransMemoryQuery buildTMQuery(HTextFlow textFlow, HasSearchType.SearchType searchType, boolean checkContext, boolean checkDocument, boolean checkProject, - boolean includeOwnTranslation) { + boolean includeOwnTranslation, + List fromVersions) { TransMemoryQuery.Condition project = new TransMemoryQuery.Condition( checkProject, textFlow.getDocument().getProjectIteration() .getProject().getId().toString()); @@ -262,7 +273,7 @@ private TransMemoryQuery buildTMQuery(HTextFlow textFlow, project, document, res); } else { query = new TransMemoryQuery(textFlow.getContents(), searchType, - project, document, res); + project, document, res, fromVersions); } if (!includeOwnTranslation) { query.setIncludeOwnTranslation(false, textFlow.getId().toString()); @@ -339,9 +350,12 @@ private void processIndexMatch(TransMemoryQuery transMemoryQuery, textFlowContents, MINIMUM_SIMILARITY); return; } + Long fromVersionId = textFlowTarget.getTextFlow().getDocument() + .getProjectIteration().getId(); TransMemoryResultItem item = createOrGetResultItem(matchesMap, match, matchType, - textFlowContents, targetContents, percent); + textFlowContents, targetContents, percent, + fromVersionId); addTextFlowTargetToResultMatches(textFlowTarget, item); } else if (entity instanceof TransMemoryUnit) { TransMemoryUnit transUnit = (TransMemoryUnit) entity; @@ -360,7 +374,7 @@ private void processIndexMatch(TransMemoryQuery transMemoryQuery, } TransMemoryResultItem item = createOrGetResultItem(matchesMap, match, TransMemoryResultItem.MatchType.Imported, - sourceContents, targetContents, percent); + sourceContents, targetContents, percent, null); addTransMemoryUnitToResultMatches(item, transUnit); } } @@ -422,13 +436,13 @@ private TransMemoryResultItem createOrGetResultItem( Map matchesMap, Object[] match, TransMemoryResultItem.MatchType matchType, ArrayList sourceContents, ArrayList targetContents, - double percent) { + double percent, Long fromVersionId) { TMKey key = new TMKey(sourceContents, targetContents); TransMemoryResultItem item = matchesMap.get(key); if (item == null) { float score = (Float) match[0]; item = new TransMemoryResultItem(sourceContents, targetContents, - matchType, score, percent); + matchType, score, percent, fromVersionId); matchesMap.put(key, item); } return item; @@ -465,9 +479,17 @@ private void addTextFlowTargetToResultMatches( * NB just because this Comparator returns 0 doesn't mean the matches are * identical. */ - private static enum TransMemoryResultComparator - implements Comparator { - COMPARATOR; + private static class TransMemoryResultComparator + implements Comparator, Serializable { + + + private static final long serialVersionUID = 1L; + private final List fromVersionIds; + + public TransMemoryResultComparator( + @Nullable List fromVersionIds) { + this.fromVersionIds = fromVersionIds; + } @Override public int compare(TransMemoryResultItem m1, TransMemoryResultItem m2) { @@ -483,7 +505,37 @@ public int compare(TransMemoryResultItem m1, TransMemoryResultItem m2) { // sort longer string lists first (more plural forms) return result; } - return m2.getMatchType().compareTo(m1.getMatchType()); + result = m2.getMatchType().compareTo(m1.getMatchType()); + if (result != 0) { + // sort match type + return result; + } + // if TM is from TMX, getFromVersionId is null + // if fromVersionIds is empty, we have no restriction on source version + if (m2.getFromVersionId() != null && + m1.getFromVersionId() != null && fromVersionIds != null && + !fromVersionIds.isEmpty()) { + int indexOfM2 = fromVersionIds.indexOf(m2.getFromVersionId()); + int indexOfM1 = fromVersionIds.indexOf(m1.getFromVersionId()); + // sort higher when index is lower + // if index is -1, something wrong with our lucene query or index + if (indexOfM1 < 0 || indexOfM2 < 0) { + log.warn("Having TM result not from requested source versions:{}", fromVersionIds); + if (indexOfM1 < 0 && indexOfM2 >= 0) { + // m2 rank higher since it's from the defined source versions + return 1; + } else if (indexOfM2 < 0 && indexOfM1 >= 0) { + // m1 is from defined source versions + return -1; + } else { + // they are both not from defined source versions + return result; + } + + } + return Integer.compare(indexOfM1, indexOfM2); + } + return result; } private int compare(List list1, List list2) { @@ -780,6 +832,18 @@ private void buildContextQuery(BooleanQuery query, query.add(resIdQuery, BooleanClause.Occur.SHOULD); } } + if (queryParams.getFromVersionIds() != null && !queryParams.getFromVersionIds().isEmpty()) { + BooleanQuery.Builder fromVersions = new BooleanQuery.Builder(); + queryParams.getFromVersionIds().forEach(projectIterationId -> { + TermQuery fromVersionQuery = new TermQuery( + new Term(IndexFieldLabels.PROJECT_VERSION_ID_FIELD, + projectIterationId.toString())); + + + fromVersions.add(fromVersionQuery, BooleanClause.Occur.SHOULD); + }); + query.add(fromVersions.build(), BooleanClause.Occur.MUST); + } } private Query buildContentQuery(TransMemoryQuery query, diff --git a/server/zanata-war/src/main/java/org/zanata/util/TransMemoryMergeStatusResolver.java b/server/zanata-war/src/main/java/org/zanata/util/TransMemoryMergeStatusResolver.java index 42b7048bf4..d8c0ea1617 100644 --- a/server/zanata-war/src/main/java/org/zanata/util/TransMemoryMergeStatusResolver.java +++ b/server/zanata-war/src/main/java/org/zanata/util/TransMemoryMergeStatusResolver.java @@ -26,7 +26,7 @@ import org.zanata.model.HTextFlowTarget; import org.zanata.webtrans.shared.model.TransMemoryDetails; import org.zanata.webtrans.shared.model.TransMemoryResultItem; -import org.zanata.webtrans.shared.rest.dto.TransMemoryMergeRequest; +import org.zanata.webtrans.shared.rest.dto.HasTMMergeCriteria; import org.zanata.webtrans.shared.rpc.MergeRule; import com.google.common.base.Objects; @@ -64,7 +64,7 @@ public static TransMemoryMergeStatusResolver newInstance() { * @return content state to be set on auto translated target. If null means * we want to reject the auto translation via TM merge */ - public ContentState decideStatus(TransMemoryMergeRequest action, + public ContentState decideStatus(HasTMMergeCriteria action, HTextFlow tfToBeFilled, TransMemoryDetails tmDetail, TransMemoryResultItem tmResult, HTextFlowTarget oldTarget) { @@ -101,7 +101,7 @@ public ContentState decideStatus(TransMemoryMergeRequest action, * @return content state to be set on auto translated target. If null means * we want to reject the auto translation via TM merge */ - public ContentState decideStatus(TransMemoryMergeRequest action, + public ContentState decideStatus(HasTMMergeCriteria action, TransMemoryResultItem tmResult, HTextFlowTarget oldTarget) { if ((int) tmResult.getSimilarityPercent() != 100) { @@ -123,14 +123,14 @@ public ContentState decideStatus(TransMemoryMergeRequest action, return ContentState.Translated; } - private void compareTextFlowResId(TransMemoryMergeRequest action, + private void compareTextFlowResId(HasTMMergeCriteria action, HTextFlow tfToBeFilled, TransMemoryDetails tmDetail) { if (notEqual(tfToBeFilled.getResId(), tmDetail.getResId())) { setFlagsBasedOnOption(action.getDifferentContextRule()); } } - private void compareTextFlowMsgContext(TransMemoryMergeRequest action, + private void compareTextFlowMsgContext(HasTMMergeCriteria action, HTextFlow tfToBeFilled, TransMemoryDetails tmDetail) { String msgCtx = null; if (tfToBeFilled.getPotEntryData() != null) { @@ -141,7 +141,7 @@ private void compareTextFlowMsgContext(TransMemoryMergeRequest action, } } - private void compareDocId(TransMemoryMergeRequest action, HTextFlow tfToBeFilled, + private void compareDocId(HasTMMergeCriteria action, HTextFlow tfToBeFilled, TransMemoryDetails tmDetail) { if (notEqual(tfToBeFilled.getDocument().getDocId(), tmDetail.getDocId())) { @@ -149,7 +149,7 @@ private void compareDocId(TransMemoryMergeRequest action, HTextFlow tfToBeFilled } } - private void compareProjectName(TransMemoryMergeRequest action, + private void compareProjectName(HasTMMergeCriteria action, HTextFlow tfToBeFilled, TransMemoryDetails tmDetail) { if (notEqual(tfToBeFilled.getDocument().getProjectIteration() .getProject().getName(), tmDetail.getProjectName())) { diff --git a/server/zanata-war/src/main/webapp/iteration/view.xhtml b/server/zanata-war/src/main/webapp/iteration/view.xhtml index 387ab3346e..cda50c2606 100644 --- a/server/zanata-war/src/main/webapp/iteration/view.xhtml +++ b/server/zanata-war/src/main/webapp/iteration/view.xhtml @@ -596,6 +596,8 @@ +
    + diff --git a/server/zanata-war/src/test/java/org/zanata/dao/ProjectDAOJPATest.java b/server/zanata-war/src/test/java/org/zanata/dao/ProjectDAOJPATest.java new file mode 100644 index 0000000000..74d8b2acb4 --- /dev/null +++ b/server/zanata-war/src/test/java/org/zanata/dao/ProjectDAOJPATest.java @@ -0,0 +1,89 @@ +package org.zanata.dao; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.persistence.EntityManager; + +import org.apache.lucene.queryparser.classic.ParseException; +import org.hibernate.search.jpa.Search; +import org.jglue.cdiunit.InRequestScope; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.zanata.ZanataJpaTest; +import org.zanata.model.HProject; +import org.zanata.security.ZanataIdentity; +import com.google.common.collect.Lists; + + +public class ProjectDAOJPATest extends ZanataJpaTest { + + private ProjectDAO dao; + + @BeforeClass + public static void disableSecurity() { + ZanataIdentity.setSecurityEnabled(false); + } + + @Before + public void setUp() { + dao = new ProjectDAO(Search.getFullTextEntityManager(getEm()), getSession()); + } + + private static void makeProject(String slug, String name, String description, + EntityManager em) { + HProject hProject = new HProject(); + hProject.setSlug(slug); + hProject.setName(name); + hProject.setDescription(description); + em.persist(hProject); + } + + private void doInTransaction(Consumer function) { + EntityManager em = getEmf().createEntityManager(); + em.getTransaction().begin(); + function.accept(em); + em.getTransaction().commit(); + } + + @After + public void cleanUp() { + deleteAllTables(); + } + + @Test + @InRequestScope + public void canDoFullTextSearch() throws Exception { + String slug = "Sample-Project"; + // hibernate search only works with transaction in place + doInTransaction( + em -> { + makeProject(slug, "Sample Project", "An example project", + em); + makeProject("another-project", "another Project", + "Another project", em); + }); + + HProject expected = dao.getBySlug(slug); + + assertThat(searchProjects("blah")).isEmpty(); + assertThat(searchProjects("sam")).containsExactly(expected); + assertThat(searchProjects("SaM")).containsExactly(expected); + assertThat(searchProjects("sampl ")).containsExactly(expected); + assertThat(searchProjects("sample-")).containsExactly(expected); + assertThat(searchProjects("proj")) + .as("wildcard search is only applied to full slug") + .isEmpty(); + assertThat(searchProjects("example project")).containsExactly(expected) + .as("search by description"); + } + + private List searchProjects(String sam) throws ParseException { + return dao.searchProjects(sam, 10, 0, false); + } +} diff --git a/server/zanata-war/src/test/java/org/zanata/rest/editor/service/TransMemoryMergeManagerTest.java b/server/zanata-war/src/test/java/org/zanata/rest/editor/service/TransMemoryMergeManagerTest.java index 94e641e727..55a475869f 100644 --- a/server/zanata-war/src/test/java/org/zanata/rest/editor/service/TransMemoryMergeManagerTest.java +++ b/server/zanata-war/src/test/java/org/zanata/rest/editor/service/TransMemoryMergeManagerTest.java @@ -17,6 +17,7 @@ import org.zanata.common.ProjectType; import org.zanata.model.HAccount; import org.zanata.model.TestFixture; +import org.zanata.security.ZanataIdentity; import org.zanata.rest.editor.service.TransMemoryMergeManager.TMMergeForDocTaskKey; import org.zanata.service.TransMemoryMergeService; import org.zanata.webtrans.shared.auth.EditorClientId; @@ -40,14 +41,14 @@ public class TransMemoryMergeManagerTest { @Captor private ArgumentCaptor taskKeyCaptor; private TransMemoryMergeCancelRequest cancelRequest; + @Mock private ZanataIdentity identity; @Before public void setUp() { MockitoAnnotations.initMocks(this); - authenticated = new HAccount(); - authenticated.setUsername("admin"); + manager = new TransMemoryMergeManager(asyncTaskHandleManager, - transMemoryMergeService, authenticated); + transMemoryMergeService, identity); WorkspaceId workspaceId = TestFixture.workspaceId(LocaleId.DE, "project", "version", ProjectType.Gettext); @@ -61,6 +62,8 @@ public void setUp() { cancelRequest = new TransMemoryMergeCancelRequest( workspaceId.getProjectIterationId(), documentId, workspaceId.getLocaleId()); + + when(identity.getAccountUsername()).thenReturn("admin"); } @Test @@ -73,7 +76,7 @@ public void setUp() { handleCaptor.capture(), taskKeyCaptor.capture()); TransMemoryMergeTaskHandle handle = handleCaptor.getValue(); assertThat(handle.getTriggeredBy()) - .isEqualTo(authenticated.getUsername()); + .isEqualTo(identity.getAccountUsername()); Mockito.verify(transMemoryMergeService).executeMergeAsync(request, handle); } @@ -102,7 +105,7 @@ public boolean isCancelled() { handleCaptor.capture(), taskKeyCaptor.capture()); TransMemoryMergeTaskHandle handle = handleCaptor.getValue(); assertThat(handle.getTriggeredBy()) - .isEqualTo(authenticated.getUsername()); + .isEqualTo(identity.getAccountUsername()); Mockito.verify(transMemoryMergeService).executeMergeAsync(request, handle); } @@ -131,7 +134,7 @@ public boolean isDone() { handleCaptor.capture(), taskKeyCaptor.capture()); TransMemoryMergeTaskHandle handle = handleCaptor.getValue(); assertThat(handle.getTriggeredBy()) - .isEqualTo(authenticated.getUsername()); + .isEqualTo(identity.getAccountUsername()); Mockito.verify(transMemoryMergeService).executeMergeAsync(request, handle); } @@ -229,7 +232,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { return true; } }; - existingHandle.setTriggeredBy(authenticated.getUsername()); + existingHandle.setTriggeredBy(identity.getAccountUsername()); TMMergeForDocTaskKey taskKey = new TMMergeForDocTaskKey( diff --git a/server/zanata-war/src/test/java/org/zanata/rest/service/ProjectVersionTest.java b/server/zanata-war/src/test/java/org/zanata/rest/service/ProjectVersionTest.java index 0261c613a1..67490088d3 100644 --- a/server/zanata-war/src/test/java/org/zanata/rest/service/ProjectVersionTest.java +++ b/server/zanata-war/src/test/java/org/zanata/rest/service/ProjectVersionTest.java @@ -1,5 +1,20 @@ package org.zanata.rest.service; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.FORBIDDEN; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; + +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.ws.rs.core.Response; + import org.apache.deltaspike.core.spi.scope.window.WindowContext; import org.dbunit.operation.DatabaseOperation; import org.hibernate.Session; @@ -10,12 +25,18 @@ import org.mockito.Mock; import org.zanata.ApplicationConfiguration; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.async.AsyncTaskHandle; +import org.zanata.common.LocaleId; import org.zanata.i18n.Messages; import org.zanata.jpa.FullText; import org.zanata.model.HAccount; +import org.zanata.model.HLocale; import org.zanata.rest.dto.User; +import org.zanata.rest.dto.VersionTMMerge; +import org.zanata.rest.editor.service.TransMemoryMergeManager; import org.zanata.rest.editor.service.resource.UserResource; import org.zanata.seam.security.IdentityManager; +import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; import org.zanata.service.ConfigurationService; import org.zanata.service.GravatarService; @@ -26,14 +47,7 @@ import org.zanata.test.CdiUnitRunner; import org.zanata.util.DefaultLocale; import org.zanata.util.UrlUtil; - -import javax.enterprise.inject.Produces; -import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.ws.rs.core.Response; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.zanata.webtrans.shared.rpc.MergeRule; /** * @author Alex Eng aeng@redhat.com @@ -59,6 +73,9 @@ public class ProjectVersionTest extends ZanataDbunitJpaTest { @Produces @Mock WindowContext windowContext; @Produces @Mock UrlUtil urlUtil; @Produces @Mock IdentityManager identityManager; + @Produces @Mock ZanataIdentity identity; + + @Produces @Mock TransMemoryMergeManager transMemoryMergeManager; @Override @Produces @@ -115,4 +132,92 @@ public void getContributors2() { List userList = (List) response.getEntity(); assertThat(userList).isEmpty(); } + + @Test + @InRequestScope + public void versionTMMergeReturnsBadRequestIfMatchThresholdIsNotValid() { + String projectSlug = "sample-project"; + String versionSlug = "2.0"; + VersionTMMerge request1 = new VersionTMMerge(LocaleId.FR, 79, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + assertThat(service.prefillWithTM(projectSlug, versionSlug, request1) + .getStatus()).isEqualTo(BAD_REQUEST.getStatusCode()); + + VersionTMMerge request2 = new VersionTMMerge(LocaleId.FR, 101, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + assertThat(service.prefillWithTM(projectSlug, versionSlug, request2) + .getStatus()).isEqualTo(BAD_REQUEST.getStatusCode()); + } + + @Test + @InRequestScope + public void versionTMMergeReturnsNotFoundIfProjectIsNotActive() { + String projectSlug = "non-existing-project"; + String versionSlug = "2.0"; + VersionTMMerge mergeRequest = new VersionTMMerge(LocaleId.FR, 90, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + + assertThat(service.prefillWithTM(projectSlug, versionSlug, mergeRequest) + .getStatus()).isEqualTo(NOT_FOUND.getStatusCode()); + } + + @Test + @InRequestScope + public void versionTMMergeReturnsNotFoundIfProjectVersionIsObsolete() { + String projectSlug = "sample-project"; + + VersionTMMerge mergeRequest = new VersionTMMerge(LocaleId.FR, 90, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + + assertThat(service.prefillWithTM(projectSlug, "non-exist", mergeRequest) + .getStatus()).isEqualTo(NOT_FOUND.getStatusCode()); + } + + @Test + @InRequestScope + public void versionTMMergeReturnsForbiddenIfProjectVersionIsReadOnly() { + String projectSlug = "sample-project"; + String versionSlug = "readonly"; + + VersionTMMerge mergeRequest = new VersionTMMerge(LocaleId.FR, 90, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + + assertThat(service.prefillWithTM(projectSlug, versionSlug, mergeRequest) + .getStatus()).isEqualTo(FORBIDDEN.getStatusCode()); + } + + @Test + @InRequestScope + public void versionTMMergeReturnsNotFoundIfLocaleIdCanNotBeFound() { + String projectSlug = "sample-project"; + String versionSlug = "2.0"; + + VersionTMMerge mergeRequest = new VersionTMMerge(LocaleId.FR, 90, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + when(localeService.getByLocaleId(LocaleId.FR)).thenReturn(null); + + assertThat(service.prefillWithTM(projectSlug, versionSlug, mergeRequest) + .getStatus()).isEqualTo(NOT_FOUND.getStatusCode()); + } + + @Test + @InRequestScope + public void versionTMMergeReturnsAcceptedIfEverythingIsGood() { + String projectSlug = "sample-project"; + String versionSlug = "2.0"; + + VersionTMMerge mergeRequest = new VersionTMMerge(LocaleId.FR, 90, + MergeRule.FUZZY, MergeRule.FUZZY, MergeRule.FUZZY, + Collections.emptyList()); + + when(transMemoryMergeManager.start(2L, mergeRequest)).thenReturn(new AsyncTaskHandle<>()); + assertThat(service.prefillWithTM(projectSlug, versionSlug, mergeRequest) + .getStatus()).isEqualTo(NOT_FOUND.getStatusCode()); + } } diff --git a/server/zanata-war/src/test/java/org/zanata/service/impl/MergeTranslationsServiceImplTest.java b/server/zanata-war/src/test/java/org/zanata/service/impl/MergeTranslationsServiceImplTest.java index a946ebbbed..a38fb06d44 100644 --- a/server/zanata-war/src/test/java/org/zanata/service/impl/MergeTranslationsServiceImplTest.java +++ b/server/zanata-war/src/test/java/org/zanata/service/impl/MergeTranslationsServiceImplTest.java @@ -34,6 +34,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.zanata.ZanataDbunitJpaTest; +import org.zanata.action.MergeTranslationsManager; import org.zanata.async.handle.MergeTranslationsTaskHandle; import org.zanata.cache.InfinispanTestCacheContainer; import org.zanata.cdi.TestTransaction; @@ -52,6 +53,7 @@ import org.zanata.model.type.TranslationSourceType; import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; +import org.zanata.service.TransMemoryMergeService; import org.zanata.service.VersionLocaleKey; import org.zanata.test.CdiUnitRunner; import org.zanata.transaction.TransactionUtilImpl; @@ -78,6 +80,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.zanata.action.MergeTranslationsManager.MergeVersionKey.getKey; @RunWith(CdiUnitRunner.class) @SupportDeltaspikeCore @@ -102,9 +105,6 @@ public class MergeTranslationsServiceImplTest extends ZanataDbunitJpaTest { @Inject TextFlowTargetDAO textFlowTargetDAO; - @Produces @Mock - private ZanataIdentity identity; - @Produces @Mock @Authenticated HAccount authenticatedAccount; @@ -133,6 +133,9 @@ public class MergeTranslationsServiceImplTest extends ZanataDbunitJpaTest { @Produces @Mock private CacheLoader versionStatisticLoader; + @Produces @Mock + private TransMemoryMergeService transMemoryMergeService; + private UserTransaction tx; private final String projectSlug = "sample-project"; @@ -179,11 +182,12 @@ public void testMergeVersionNotExist() throws Exception { String sourceVersionSlug = "1.0"; String targetVersionSlug = "non-exist-version"; - MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle(); + MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle( + getKey(projectSlug, targetVersionSlug)); Future future = service.startMergeTranslations(projectSlug, sourceVersionSlug, projectSlug, targetVersionSlug, true, handle); - verifyZeroInteractions(identity); + verifyZeroInteractions(authenticatedAccount); // wait for the async process to finish... future.get(); // No translations were performed @@ -196,10 +200,11 @@ public void testMergeEmptyDoc() throws Exception { String sourceVersionSlug = "1.0"; String targetVersionSlug = "3.0"; - MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle(); + MergeTranslationsTaskHandle handle = new MergeTranslationsTaskHandle( + getKey(projectSlug, targetVersionSlug)); Future future = service.startMergeTranslations(projectSlug, sourceVersionSlug, projectSlug, targetVersionSlug, true, null); - verifyZeroInteractions(identity); + verifyZeroInteractions(authenticatedAccount); // wait for the async process to finish... future.get(); // No translations were performed diff --git a/server/zanata-war/src/test/java/org/zanata/service/impl/TransMemoryMergeServiceImplTest.java b/server/zanata-war/src/test/java/org/zanata/service/impl/TransMemoryMergeServiceImplTest.java index 7175ba65d3..6a1b48d7d3 100644 --- a/server/zanata-war/src/test/java/org/zanata/service/impl/TransMemoryMergeServiceImplTest.java +++ b/server/zanata-war/src/test/java/org/zanata/service/impl/TransMemoryMergeServiceImplTest.java @@ -32,9 +32,11 @@ import static org.zanata.webtrans.shared.rpc.HasSearchType.SearchType.FUZZY_PLURAL; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; +import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Produces; import javax.inject.Inject; @@ -46,12 +48,12 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.zanata.async.handle.TransMemoryMergeTaskHandle; import org.zanata.common.ContentState; import org.zanata.common.LocaleId; import org.zanata.common.ProjectType; +import org.zanata.dao.ProjectIterationDAO; import org.zanata.dao.TextFlowDAO; import org.zanata.dao.TransMemoryUnitDAO; import org.zanata.model.HAccount; @@ -60,11 +62,13 @@ import org.zanata.model.TestFixture; import org.zanata.model.tm.TransMemoryUnit; import org.zanata.model.type.TranslationSourceType; +import org.zanata.security.ZanataIdentity; import org.zanata.security.annotations.Authenticated; import org.zanata.service.LocaleService; import org.zanata.service.SecurityService; import org.zanata.service.TranslationMemoryService; import org.zanata.service.TranslationService; +import org.zanata.service.VersionStateCache; import org.zanata.test.CdiUnitRunner; import org.zanata.transaction.TransactionUtil; import org.zanata.transaction.TransactionUtilForUnitTest; @@ -124,6 +128,13 @@ public class TransMemoryMergeServiceImplTest { @Produces TransactionUtil transactionUtil = new TransactionUtilForUnitTest(null); + @Produces @Mock + private VersionStateCache versionStateCacheImpl; + @Produces @Mock + private ZanataIdentity identity; + @Produces @Mock + private ProjectIterationDAO projectIterationDAO; + private String projectSlug = "projectSlug"; private String versionSlug = "versionSlug"; private String docId = "pot/a.po"; @@ -140,6 +151,7 @@ public class TransMemoryMergeServiceImplTest { private final EditorClientId editorClientId = new EditorClientId("sessionId", 1); private FilterConstraints untranslatedFilter; + private List fromVersions; private TransMemoryMergeRequest prepareAction(int threshold, MergeOptions opts) { LocaleId localeId = targetLocale.getLocaleId(); @@ -173,7 +185,7 @@ private TransMemoryMergeRequest prepareAction(int threshold, private static TransMemoryResultItem tmResult(Long sourceId, int percent) { TransMemoryResultItem resultItem = new TransMemoryResultItem(tmSource, tmTarget, - MatchType.ApprovedInternal, 1D, percent); + MatchType.ApprovedInternal, 1D, percent, 1L); resultItem.addSourceId(sourceId); return resultItem; } @@ -182,7 +194,7 @@ private static TransMemoryResultItem importedTmResult(Long sourceId, int percent) { TransMemoryResultItem resultItem = new TransMemoryResultItem(tmSource, tmTarget, - MatchType.Imported, 1D, percent); + MatchType.Imported, 1D, percent, 1L); resultItem.addSourceId(sourceId); return resultItem; } @@ -201,7 +213,7 @@ private static TransMemoryDetails tmDetail() { return new TransMemoryQuery(contents, searchType, new TransMemoryQuery.Condition(false, projectSlug), new TransMemoryQuery.Condition(false, docId), - new TransMemoryQuery.Condition(false, resId)); + new TransMemoryQuery.Condition(false, resId), fromVersions); } else { TransMemoryQuery.Condition projectCondition = new TransMemoryQuery.Condition( @@ -218,13 +230,14 @@ private static TransMemoryDetails tmDetail() { opts.getDifferentResId() == MergeRule.REJECT, resId); return new TransMemoryQuery(contents, searchType, projectCondition, - documentCondition, resCondition); + documentCondition, resCondition, fromVersions); } } @Before public void setUp() throws NoSuchWorkspaceException { MockitoAnnotations.initMocks(this); + fromVersions = Collections.emptyList(); asyncTaskHandle = new TransMemoryMergeTaskHandle(); WorkspaceId workspaceId = new WorkspaceId(projectIterationId, targetLocale.getLocaleId()); @@ -275,7 +288,8 @@ public void willTranslateIfMatches() throws ActionException { when( translationMemoryService.searchBestMatchTransMemory(hTextFlow, targetLocale.getLocaleId(), sourceLocale.getLocaleId(), - false, false, false, action.getThresholdPercent())) + false, false, false, action.getThresholdPercent(), + fromVersions)) .thenReturn(matches); // When: execute the action @@ -320,7 +334,8 @@ public void willNotTranslateIfNoMatches() throws ActionException { when( translationMemoryService.searchBestMatchTransMemory(hTextFlow, targetLocale.getLocaleId(), sourceLocale.getLocaleId(), - false, false, false, action.getThresholdPercent())) + false, false, false, action.getThresholdPercent(), + fromVersions)) .thenReturn(matches); when(localeService.getByLocaleId(action.localeId)) @@ -388,22 +403,26 @@ public void canHandleMultipleTextFlows() throws ActionException { when( translationMemoryService.searchBestMatchTransMemory( textFlow100TM, targetLocale.getLocaleId(), - sourceLocale.getLocaleId(), false, false, false, 90)) + sourceLocale.getLocaleId(), false, false, false, 90, + fromVersions)) .thenReturn(tm100); when( translationMemoryService.searchBestMatchTransMemory( textFLow90TM, targetLocale.getLocaleId(), - sourceLocale.getLocaleId(), false, false, false, 90)) + sourceLocale.getLocaleId(), false, false, false, 90, + fromVersions)) .thenReturn(tm90); when( translationMemoryService.searchBestMatchTransMemory( textFlow80TM, targetLocale.getLocaleId(), - sourceLocale.getLocaleId(), false, false, false, 90)) + sourceLocale.getLocaleId(), false, false, false, 90, + fromVersions)) .thenReturn(tm80); when( translationMemoryService.searchBestMatchTransMemory( textFlowNoTM, targetLocale.getLocaleId(), - sourceLocale.getLocaleId(), false, false, false, 90)) + sourceLocale.getLocaleId(), false, false, false, 90, + fromVersions)) .thenReturn(noMatch); when(textFlowDAO.findById(tmResultSource.getId(), false)).thenReturn( @@ -483,7 +502,8 @@ public void canAutoTranslateImportedTMResults() throws Exception { when( translationMemoryService.searchBestMatchTransMemory(hTextFlow, targetLocale.getLocaleId(), sourceLocale.getLocaleId(), - false, false, false, action.getThresholdPercent())) + false, false, false, action.getThresholdPercent(), + fromVersions)) .thenReturn(match); when(transMemoryUnitDAO.findById(tuResultSource.getId())).thenReturn( tuResultSource); diff --git a/server/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java b/server/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java index c15f8eaf2d..f43883a105 100644 --- a/server/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java +++ b/server/zanata-war/src/test/java/org/zanata/service/impl/TranslationMemoryServiceImplTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.zanata.test.rule.FunctionalTestRule.reentrant; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; @@ -210,7 +211,7 @@ private void executeFindBestTMMatch(HTextFlow textFlow, int threshold, service.searchBestMatchTransMemory(textFlow, targetLocale.getLocaleId(), sourceLocale.getLocaleId(), false, false, false, - threshold); + threshold, Collections.emptyList()); assertThat(match.isPresent()).isEqualTo(hasMatch); } // to check if any of the sourceContents contain searchString diff --git a/server/zanata-war/src/test/java/org/zanata/util/TransMemoryMergeStatusResolverTest.java b/server/zanata-war/src/test/java/org/zanata/util/TransMemoryMergeStatusResolverTest.java index 59a92e6d42..52aa5cb9d3 100644 --- a/server/zanata-war/src/test/java/org/zanata/util/TransMemoryMergeStatusResolverTest.java +++ b/server/zanata-war/src/test/java/org/zanata/util/TransMemoryMergeStatusResolverTest.java @@ -78,13 +78,13 @@ public void beforeMethod() { private static TransMemoryResultItem tmResultWithSimilarity(double percent) { return new TransMemoryResultItem(null, null, - MatchType.TranslatedInternal, 0, percent); + MatchType.TranslatedInternal, 0, percent, 1L); } private static TransMemoryResultItem tmResultWithSimilarityAndExternallyImported(double percent) { return new TransMemoryResultItem(null, null, MatchType.Imported, 0, - percent); + percent, null); } private static TransMemoryDetails tmDetail(String projectName, diff --git a/server/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/TransMemoryPresenterTest.java b/server/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/TransMemoryPresenterTest.java index bc55120b26..e634db8928 100644 --- a/server/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/TransMemoryPresenterTest.java +++ b/server/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/TransMemoryPresenterTest.java @@ -26,6 +26,7 @@ import org.zanata.common.ProjectType; import org.zanata.model.TestFixture; import org.zanata.rest.dto.stats.ContainerTranslationStatistics; +import org.zanata.service.impl.TranslationMemoryServiceImpl; import org.zanata.webtrans.client.events.CopyDataToEditorEvent; import org.zanata.webtrans.client.events.TransMemoryShortcutCopyEvent; import org.zanata.webtrans.client.events.TransUnitSelectionEvent; @@ -156,7 +157,7 @@ public void showTMDetails() { TransMemoryResultItem object = new TransMemoryResultItem(new ArrayList(), new ArrayList(), MatchType.ApprovedInternal, 0, - 0); + 0, null); when(display.getSearchType()).thenReturn(searchType); presenter.showTMDetails(object); @@ -169,7 +170,7 @@ public void fireCopyEvent() { TransMemoryResultItem object = new TransMemoryResultItem(new ArrayList(), new ArrayList(), MatchType.ApprovedInternal, 0, - 0); + 0, null); ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(CopyDataToEditorEvent.class); @@ -512,7 +513,7 @@ public void ignoreIfNotEditorConfigOptionChange() { * it. * * @throws Exception - * @see org.zanata.webtrans.server.rpc.GetTransMemoryHandler.TransMemoryResultComparator + * @see TranslationMemoryServiceImpl.TransMemoryResultComparator */ @Test public void matchTypeEnumOrder() throws Exception { diff --git a/server/zanata-war/src/test/resources/org/zanata/test/model/CopyVersionData.dbunit.xml b/server/zanata-war/src/test/resources/org/zanata/test/model/CopyVersionData.dbunit.xml index c18b44502c..11633c2c74 100644 --- a/server/zanata-war/src/test/resources/org/zanata/test/model/CopyVersionData.dbunit.xml +++ b/server/zanata-war/src/test/resources/org/zanata/test/model/CopyVersionData.dbunit.xml @@ -19,6 +19,10 @@ creationDate="2009-09-01 20:40:46" lastChanged="2009-09-01 20:40:46" parentId="[NULL]" project_id="1" overrideLocales="TRUE" status="A" /> + +