├── .gitignore ├── .mailmap ├── README.md ├── license ├── nbactions.xml ├── pom.xml ├── src ├── main │ ├── java │ │ └── dev │ │ │ └── mbien │ │ │ └── xpathutil │ │ │ ├── XPathDataObject.java │ │ │ ├── XPathEditorKit.java │ │ │ ├── XPathEvalAction.java │ │ │ ├── completion │ │ │ ├── XPathCompletionItem.java │ │ │ └── XPathCompletionProvider.java │ │ │ └── ui │ │ │ ├── XPathEvaluator.java │ │ │ ├── XPathEvaluatorThread.java │ │ │ ├── XPathTopComponent.form │ │ │ └── XPathTopComponent.java │ ├── nbm │ │ └── manifest.mf │ └── resources │ │ └── dev │ │ └── mbien │ │ └── xpathutil │ │ ├── Bundle.properties │ │ └── ui │ │ ├── attribute.png │ │ ├── element.png │ │ └── utilities-terminal.png └── test │ └── java │ └── dev │ └── mbien │ └── xpathutil │ └── ui │ └── XPathEvaluatorTest.java └── xpath-util.png /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **~ 3 | **/nbproject/private/ 4 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Michael Bien 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xpath-util 2 | XPath utility plugin for NetBeans. Provides a window for simple xpath queries with auto completion and displays the results. 3 | 4 | ![screenshot](xpath-util.png "xpath utility window") -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | -------------------------------------------------------------------------------- /nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-clean deploy 5 | clean deploy 6 | 7 | clean 8 | deploy 9 | 10 | 11 | release 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | NetBeans XPath Utility 6 | Provides xpath search functionality 7 | https://github.com/mbien/xpath-util 8 | 9 | dev.mbien.netbeans.xpath 10 | xpath-util 11 | 1.6-SNAPSHOT 12 | nbm 13 | 14 | 15 | 16 | LGPL 2.1 License 17 | https://opensource.org/licenses/LGPL-2.1 18 | 19 | 20 | 21 | 22 | 23 | Michael Bien 24 | mbien42@gmail.com 25 | Freelancer 26 | https://mbien.dev 27 | 28 | 29 | 30 | 31 | scm:git@github.com:mbien/xpath-util.git 32 | scm:git@github.com:mbien/xpath-util.git 33 | https://github.com/mbien/xpath-util 34 | 35 | 36 | 37 | UTF-8 38 | RELEASE220 39 | 17 40 | 41 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-compiler-plugin 54 | 3.13.0 55 | 56 | 57 | -Xlint 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-jar-plugin 64 | 3.4.2 65 | 66 | 67 | ${project.build.outputDirectory}/META-INF/MANIFEST.MF 68 | 69 | 70 | 71 | 72 | org.apache.netbeans.utilities 73 | nbm-maven-plugin 74 | 14.1 75 | true 76 | 77 | Michael Bien 78 | LGPL 2.1 79 | ${basedir}/license 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | org.netbeans.api 88 | org-netbeans-api-annotations-common 89 | ${nbversion} 90 | 91 | 92 | org.netbeans.api 93 | org-netbeans-api-templates 94 | ${nbversion} 95 | 96 | 97 | org.netbeans.api 98 | org-openide-filesystems 99 | ${nbversion} 100 | 101 | 102 | org.netbeans.api 103 | org-openide-loaders 104 | ${nbversion} 105 | 106 | 107 | org.netbeans.api 108 | org-openide-nodes 109 | ${nbversion} 110 | 111 | 112 | org.netbeans.api 113 | org-openide-util 114 | ${nbversion} 115 | 116 | 117 | org.netbeans.api 118 | org-openide-util-lookup 119 | ${nbversion} 120 | 121 | 122 | org.netbeans.api 123 | org-openide-util-ui 124 | ${nbversion} 125 | 126 | 127 | org.netbeans.api 128 | org-openide-windows 129 | ${nbversion} 130 | 131 | 132 | org.netbeans.api 133 | org-openide-awt 134 | ${nbversion} 135 | 136 | 137 | org.netbeans.api 138 | org-openide-dialogs 139 | ${nbversion} 140 | 141 | 142 | org.netbeans.api 143 | org-openide-text 144 | ${nbversion} 145 | 146 | 147 | org.netbeans.api 148 | org-netbeans-modules-editor 149 | ${nbversion} 150 | 151 | 152 | org.netbeans.api 153 | org-netbeans-modules-editor-lib 154 | ${nbversion} 155 | 156 | 157 | org.netbeans.api 158 | org-netbeans-modules-editor-lib2 159 | ${nbversion} 160 | 161 | 162 | org.netbeans.api 163 | org-netbeans-modules-editor-completion 164 | ${nbversion} 165 | 166 | 167 | org.netbeans.api 168 | org-netbeans-api-xml-ui 169 | ${nbversion} 170 | 171 | 172 | 173 | org.junit.jupiter 174 | junit-jupiter-api 175 | 5.10.2 176 | test 177 | 178 | 179 | org.junit.jupiter 180 | junit-jupiter-params 181 | 5.10.2 182 | test 183 | 184 | 185 | org.junit.jupiter 186 | junit-jupiter-engine 187 | 5.10.2 188 | test 189 | 190 | 191 | 192 | 193 | 194 | release 195 | 196 | 197 | 198 | 199 | org.apache.maven.plugins 200 | maven-enforcer-plugin 201 | 3.5.0 202 | 203 | 204 | enforce-versions 205 | 206 | enforce 207 | 208 | 209 | 210 | 211 | 3.9.8 212 | 213 | 214 | 17 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | org.apache.maven.plugins 224 | maven-source-plugin 225 | 3.3.1 226 | 227 | 228 | attach-sources 229 | 230 | jar-no-fork 231 | 232 | 233 | 234 | 235 | 236 | 237 | org.apache.maven.plugins 238 | maven-javadoc-plugin 239 | 3.7.0 240 | 241 | 242 | attach-javadocs 243 | 244 | jar 245 | 246 | 247 | 248 | none 249 | 250 | true 251 | 252 | 253 | 254 | 255 | 256 | 257 | org.apache.maven.plugins 258 | maven-gpg-plugin 259 | 3.2.4 260 | 261 | 262 | sign-artifacts 263 | verify 264 | 265 | sign 266 | 267 | 268 | 269 | 270 | 271 | 272 | org.sonatype.plugins 273 | nexus-staging-maven-plugin 274 | 1.7.0 275 | true 276 | 277 | ossrh 278 | https://s01.oss.sonatype.org/ 279 | false 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | ossrh 291 | https://s01.oss.sonatype.org/content/repositories/snapshots 292 | 293 | 294 | ossrh 295 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/XPathDataObject.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil; 2 | 3 | import java.io.IOException; 4 | import org.openide.filesystems.FileObject; 5 | import org.openide.filesystems.MIMEResolver; 6 | import org.openide.loaders.DataNode; 7 | import org.openide.loaders.DataObject; 8 | import org.openide.loaders.DataObjectExistsException; 9 | import org.openide.loaders.MultiDataObject; 10 | import org.openide.loaders.MultiFileLoader; 11 | import org.openide.nodes.CookieSet; 12 | import org.openide.nodes.Node; 13 | import org.openide.nodes.Children; 14 | import org.openide.util.Lookup; 15 | import org.openide.text.DataEditorSupport; 16 | 17 | /** 18 | * 19 | * @author Michael Bien 20 | */ 21 | @MIMEResolver.ExtensionRegistration( 22 | displayName = "XPath File", 23 | mimeType = XPathDataObject.MIME_TYPE, 24 | extension = {"xpath"}, 25 | position = 3210 26 | ) 27 | @DataObject.Registration( 28 | mimeType = XPathDataObject.MIME_TYPE, 29 | iconBase = "dev/mbien/xpathutil/ui/utilities-terminal.png", 30 | displayName = "XPath File Loader", 31 | position = 300 32 | ) 33 | public class XPathDataObject extends MultiDataObject { 34 | 35 | private static final long serialVersionUID = 1L; 36 | 37 | public static final String MIME_TYPE = "text/x-xpath"; 38 | 39 | public XPathDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { 40 | super(pf, loader); 41 | CookieSet cookies = getCookieSet(); 42 | cookies.add((Node.Cookie) DataEditorSupport.create(this, getPrimaryEntry(), cookies)); 43 | } 44 | 45 | @Override 46 | protected Node createNodeDelegate() { 47 | return new DataNode(this, Children.LEAF, getLookup()); 48 | } 49 | 50 | @Override 51 | public Lookup getLookup() { 52 | return getCookieSet().getLookup(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/XPathEditorKit.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil; 2 | 3 | import javax.swing.text.EditorKit; 4 | import org.netbeans.api.editor.mimelookup.MimeRegistration; 5 | import org.netbeans.modules.editor.NbEditorKit; 6 | 7 | /** 8 | * @author Michael Bien 9 | */ 10 | @MimeRegistration(mimeType = XPathDataObject.MIME_TYPE, service = EditorKit.class) 11 | public class XPathEditorKit extends NbEditorKit { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | @Override 16 | public String getContentType() { 17 | return XPathDataObject.MIME_TYPE; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/XPathEvalAction.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil; 2 | 3 | import dev.mbien.xpathutil.ui.XPathTopComponent; 4 | import org.netbeans.api.xml.cookies.ValidateXMLCookie; 5 | import org.openide.awt.ActionID; 6 | import org.openide.awt.ActionReference; 7 | import org.openide.awt.ActionReferences; 8 | import org.openide.awt.ActionRegistration; 9 | import org.openide.cookies.EditorCookie; 10 | import org.openide.loaders.DataObject; 11 | import org.openide.nodes.Node; 12 | import org.openide.util.HelpCtx; 13 | import org.openide.util.NbBundle; 14 | import org.openide.util.actions.CookieAction; 15 | 16 | /** 17 | * Opens the XPath evaluator view. 18 | * @author Michael Bien 19 | */ 20 | @ActionID(id = "dev.mbien.xpathutil.XPathEvalAction", category = "XML") 21 | @ActionRegistration(displayName = "Evaluate XPath", lazy = false) 22 | @ActionReferences(value = { 23 | @ActionReference(path = "Menu/BuildProject", position = 887), 24 | @ActionReference(path = "Loaders/text/xml-mime/Actions", position = 650), 25 | @ActionReference(path = "Loaders/text/html/Actions", position = 650), 26 | @ActionReference(path = "Loaders/text/dtd-xml/Actions", position = 20050), 27 | @ActionReference(path = "Loaders/text/x-maven-pom+xml/Actions", position = 350), 28 | @ActionReference(path = "Loaders/text/xhtml/Actions", position = 885) 29 | }) 30 | @NbBundle.Messages({ 31 | "CTL_XPathEvalAction=Evaluate XPath" 32 | }) 33 | public final class XPathEvalAction extends CookieAction { 34 | 35 | private static final long serialVersionUID = 1L; 36 | 37 | @Override 38 | protected void performAction(Node[] activatedNodes) { 39 | DataObject dataObject = activatedNodes[0].getLookup().lookup(DataObject.class); 40 | if (dataObject != null) { 41 | XPathTopComponent xpathComponent = XPathTopComponent.findInstance(); 42 | xpathComponent.open(); 43 | EditorCookie file = dataObject.getLookup().lookup(EditorCookie.class); 44 | if (file != null) { 45 | file.open(); 46 | } 47 | xpathComponent.requestActive(dataObject); 48 | } 49 | } 50 | 51 | @Override 52 | protected int mode() { 53 | return CookieAction.MODE_EXACTLY_ONE; 54 | } 55 | 56 | @Override 57 | public String getName() { 58 | return NbBundle.getMessage(XPathEvalAction.class, "CTL_XPathEvalAction"); 59 | } 60 | 61 | @Override 62 | protected Class[] cookieClasses() { 63 | return new Class[] { ValidateXMLCookie.class }; 64 | } 65 | 66 | @Override 67 | protected String iconResource() { 68 | return XPathTopComponent.ICON_PATH; 69 | } 70 | 71 | @Override 72 | public HelpCtx getHelpCtx() { 73 | return HelpCtx.DEFAULT_HELP; 74 | } 75 | 76 | @Override 77 | protected boolean asynchronous() { 78 | return false; 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/completion/XPathCompletionItem.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.completion; 2 | 3 | import java.awt.Color; 4 | import java.awt.Font; 5 | import java.awt.Graphics; 6 | import java.awt.event.KeyEvent; 7 | import javax.swing.ImageIcon; 8 | import javax.swing.JToolTip; 9 | import javax.swing.text.BadLocationException; 10 | import javax.swing.text.Document; 11 | import javax.swing.text.JTextComponent; 12 | import javax.swing.text.StyledDocument; 13 | import org.netbeans.api.editor.completion.Completion; 14 | import org.netbeans.spi.editor.completion.CompletionItem; 15 | import org.netbeans.spi.editor.completion.CompletionResultSet; 16 | import org.netbeans.spi.editor.completion.CompletionTask; 17 | import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery; 18 | import org.netbeans.spi.editor.completion.support.AsyncCompletionTask; 19 | import org.netbeans.spi.editor.completion.support.CompletionUtilities; 20 | import org.openide.util.Exceptions; 21 | import org.openide.util.ImageUtilities; 22 | 23 | /** 24 | * 25 | * @author Michael Bien 26 | */ 27 | public class XPathCompletionItem implements CompletionItem { 28 | 29 | private static final ImageIcon ATTRIBUTE_ICON = ImageUtilities.loadImageIcon("dev/mbien/xpathutil/ui/attribute.png", false); 30 | private static final ImageIcon ELEMENT_ICON = ImageUtilities.loadImageIcon("dev/mbien/xpathutil/ui/element.png", false); 31 | 32 | private final String text; 33 | private final int caretOffset; 34 | private final int dotOffset; 35 | private final boolean attribute; 36 | 37 | public XPathCompletionItem(String text, int dotOffset, int caretOffset) { 38 | this.text = text; 39 | this.dotOffset = dotOffset; 40 | this.caretOffset = caretOffset; 41 | this.attribute = text.startsWith("@"); 42 | } 43 | 44 | @Override 45 | public void defaultAction(JTextComponent component) { 46 | try { 47 | StyledDocument doc = (StyledDocument) component.getDocument(); 48 | if(component.getSelectionStart() == component.getSelectionEnd()) { 49 | doc.remove(dotOffset, caretOffset - dotOffset); 50 | }else{ 51 | doc.remove(dotOffset, component.getSelectionEnd() - dotOffset); 52 | } 53 | doc.insertString(dotOffset, text, null); 54 | Completion.get().hideAll(); 55 | } catch (BadLocationException ex) { 56 | Exceptions.printStackTrace(ex); 57 | } 58 | } 59 | 60 | @Override 61 | public void processKeyEvent(KeyEvent evt) { 62 | if(evt.getKeyCode() == KeyEvent.VK_ENTER && evt.getSource() instanceof JTextComponent) 63 | defaultAction((JTextComponent) evt.getSource()); 64 | } 65 | 66 | @Override 67 | public int getPreferredWidth(Graphics graphics, Font font) { 68 | return CompletionUtilities.getPreferredWidth(text, null, graphics, font); 69 | } 70 | 71 | @Override 72 | public void render(Graphics g, Font defaultFont, Color defaultColor, Color backgroundColor, int width, int height, boolean selected) { 73 | ImageIcon icon = attribute ? ATTRIBUTE_ICON : ELEMENT_ICON; 74 | Color color = selected ? Color.WHITE : backgroundColor; 75 | String line = "" + text + ""; 76 | CompletionUtilities.renderHtml(icon, line, null, g, defaultFont, color, width, height, selected); 77 | } 78 | 79 | @Override 80 | public CompletionTask createDocumentationTask() { 81 | return null; 82 | } 83 | 84 | @Override 85 | public CompletionTask createToolTipTask() { 86 | 87 | return new AsyncCompletionTask(new AsyncCompletionQuery() { 88 | @Override 89 | protected void query(CompletionResultSet completionResultSet, Document document, int i) { 90 | JToolTip toolTip = new JToolTip(); 91 | toolTip.setTipText("Press Enter to insert \"" + text + "\""); 92 | completionResultSet.setToolTip(toolTip); 93 | completionResultSet.finish(); 94 | } 95 | }); 96 | } 97 | 98 | @Override 99 | public boolean instantSubstitution(JTextComponent component) { 100 | defaultAction(component); 101 | return true; 102 | } 103 | 104 | @Override 105 | public int getSortPriority() { 106 | return attribute ? 2 : 1; 107 | } 108 | 109 | @Override 110 | public CharSequence getSortText() { 111 | return text; 112 | } 113 | 114 | @Override 115 | public CharSequence getInsertPrefix() { 116 | return text; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/completion/XPathCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.completion; 2 | 3 | import java.io.IOException; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | import javax.swing.text.BadLocationException; 7 | import javax.swing.text.Document; 8 | import javax.swing.text.JTextComponent; 9 | import javax.xml.xpath.XPathConstants; 10 | import javax.xml.xpath.XPathExpressionException; 11 | import dev.mbien.xpathutil.XPathDataObject; 12 | import dev.mbien.xpathutil.ui.XPathEvaluator; 13 | import dev.mbien.xpathutil.ui.XPathTopComponent; 14 | import java.lang.reflect.InvocationTargetException; 15 | import javax.swing.SwingUtilities; 16 | import org.netbeans.api.editor.completion.Completion; 17 | import org.netbeans.api.editor.mimelookup.MimeRegistration; 18 | import org.netbeans.spi.editor.completion.CompletionProvider; 19 | import org.netbeans.spi.editor.completion.CompletionResultSet; 20 | import org.netbeans.spi.editor.completion.CompletionTask; 21 | import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery; 22 | import org.netbeans.spi.editor.completion.support.AsyncCompletionTask; 23 | import org.openide.util.Exceptions; 24 | import org.w3c.dom.NamedNodeMap; 25 | import org.w3c.dom.Node; 26 | import org.w3c.dom.NodeList; 27 | import org.xml.sax.SAXException; 28 | 29 | /** 30 | * 31 | * @author Michael Bien 32 | */ 33 | @MimeRegistration(mimeType = XPathDataObject.MIME_TYPE, service = CompletionProvider.class) 34 | public class XPathCompletionProvider implements CompletionProvider { 35 | 36 | @Override 37 | public CompletionTask createTask(int type, JTextComponent textComponent) { 38 | return type == CompletionProvider.COMPLETION_QUERY_TYPE || type == CompletionProvider.COMPLETION_ALL_QUERY_TYPE 39 | ? new AsyncCompletionTask(new XPathCompletionQuery(), textComponent) : null; 40 | } 41 | 42 | @Override 43 | public int getAutoQueryTypes(JTextComponent component, String str) { 44 | return str != null && (str.endsWith("/") || str.endsWith("@") || str.endsWith("[")) ? CompletionProvider.COMPLETION_QUERY_TYPE : 0; 45 | } 46 | 47 | private static class XPathCompletionQuery extends AsyncCompletionQuery { 48 | 49 | private final static XPathEvaluator eval = new XPathEvaluator(); 50 | 51 | @Override 52 | protected void query(CompletionResultSet completionResultSet, Document doc, int caretIndex) { 53 | 54 | try { 55 | String line = doc.getText(0, caretIndex); 56 | String line_stripped = line.strip(); 57 | 58 | String exp; 59 | 60 | int slashIndex = line.lastIndexOf('/'); 61 | int slashIndex_stripped = line_stripped.lastIndexOf('/'); 62 | 63 | int atIndex = line.lastIndexOf('@'); 64 | int bracketIndex = line.lastIndexOf('['); 65 | 66 | boolean in_square_bracket = bracketIndex > line.lastIndexOf(']'); 67 | 68 | int dotOffset; 69 | if (in_square_bracket) { 70 | exp = line.substring(0, bracketIndex); 71 | String subpath = line.substring(bracketIndex+1, caretIndex); 72 | if (subpath.contains("/")) { 73 | subpath = subpath.substring(0, subpath.lastIndexOf('/') + 1); 74 | if (subpath.endsWith("/") && !subpath.startsWith("@")) { 75 | exp += "/" + subpath.substring(0, subpath.length() - 1); 76 | } 77 | } 78 | dotOffset = caretIndex - line.length() + exp.length() + 1; 79 | } else if (slashIndex_stripped == 1 && line_stripped.startsWith("//")) { 80 | exp = line.substring(0, slashIndex + 1) + "*"; 81 | dotOffset = caretIndex - line.length() + exp.length() - 1; 82 | } else if (slashIndex_stripped == 0) { 83 | exp = line.substring(0, slashIndex + 1); 84 | dotOffset = caretIndex - line.length() + exp.length(); 85 | } else if (slashIndex_stripped != -1 || atIndex != -1) { 86 | exp = line.substring(0, max(atIndex - 1, slashIndex)); 87 | dotOffset = caretIndex - line.length() + exp.length() + 1; 88 | } else { 89 | exp = "/"; 90 | dotOffset = 0; 91 | } 92 | 93 | String prefix = line.substring(max(slashIndex, atIndex - 1, bracketIndex) + 1, caretIndex); 94 | if (prefix.startsWith(":")) { 95 | prefix = prefix.substring(1); 96 | } 97 | 98 | try { 99 | String xml; 100 | { 101 | String[] res = new String[1]; 102 | SwingUtilities.invokeAndWait(() -> res[0] = XPathTopComponent.findInstance().getSourceEditorText()); 103 | xml = res[0]; 104 | } 105 | 106 | NodeList list = (NodeList)eval.evaluate(exp, xml, XPathConstants.NODESET); 107 | 108 | if (list != null) { 109 | 110 | // this is faster than parsing a document but kinda hacky 111 | boolean hasNamespace = xml.contains("xmlns"); 112 | 113 | Set items = new HashSet<>(); 114 | 115 | for (int b = 0; b < list.getLength(); b++) { 116 | 117 | Node current = list.item(b); 118 | 119 | NamedNodeMap attributes = current.getAttributes(); 120 | if (attributes != null) { 121 | for(int a = 0; a < attributes.getLength(); a++) { 122 | String name = attributes.item(a).getNodeName(); 123 | if (name != null && (name = "@"+name).startsWith(prefix)) { 124 | items.add(name); 125 | } 126 | } 127 | } 128 | NodeList childNodes = current.getChildNodes(); 129 | for (int n = 0; n < childNodes.getLength(); n++) { 130 | Node item = childNodes.item(n); 131 | String name = item.getNodeName(); 132 | String localName = item.getLocalName(); 133 | if (item.getNodeType() == Node.ELEMENT_NODE && (name.startsWith(prefix) || localName.startsWith(prefix))) { 134 | if (hasNamespace && item.getPrefix() == null) { 135 | items.add(":"+name); 136 | } else { 137 | items.add(name); 138 | } 139 | } 140 | } 141 | 142 | } 143 | 144 | if (items.isEmpty() && "text()".startsWith(prefix)) { 145 | items.add("text()"); 146 | } 147 | 148 | for (String item : items) { 149 | completionResultSet.addItem(new XPathCompletionItem(item, dotOffset, caretIndex)); 150 | } 151 | } else { 152 | Completion.get().hideAll(); 153 | } 154 | 155 | } catch (SAXException | XPathExpressionException ex) { 156 | // nothing to auto complete if the document or xpath is not valid 157 | } catch (InterruptedException | InvocationTargetException ex) { 158 | Exceptions.printStackTrace(ex); 159 | } 160 | 161 | } catch (BadLocationException | IOException ex) { 162 | Exceptions.printStackTrace(ex); 163 | } 164 | 165 | completionResultSet.finish(); 166 | 167 | } 168 | 169 | private static int max(int a, int b) { 170 | return Math.max(a, b); 171 | } 172 | 173 | private static int max(int a, int b, int c) { 174 | return Math.max(Math.max(a, b), c); 175 | } 176 | } 177 | 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/ui/XPathEvaluator.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.ui; 2 | 3 | import java.io.IOException; 4 | import java.io.StringReader; 5 | import java.io.StringWriter; 6 | import java.util.Iterator; 7 | import javax.xml.XMLConstants; 8 | import javax.xml.namespace.NamespaceContext; 9 | import javax.xml.namespace.QName; 10 | import javax.xml.parsers.DocumentBuilder; 11 | import javax.xml.parsers.DocumentBuilderFactory; 12 | import javax.xml.parsers.ParserConfigurationException; 13 | import javax.xml.transform.OutputKeys; 14 | import javax.xml.transform.Transformer; 15 | import javax.xml.transform.TransformerConfigurationException; 16 | import javax.xml.transform.TransformerException; 17 | import javax.xml.transform.TransformerFactory; 18 | import javax.xml.transform.dom.DOMSource; 19 | import javax.xml.transform.stream.StreamResult; 20 | import javax.xml.xpath.XPath; 21 | import javax.xml.xpath.XPathConstants; 22 | import javax.xml.xpath.XPathExpression; 23 | import javax.xml.xpath.XPathExpressionException; 24 | import javax.xml.xpath.XPathFactory; 25 | import org.openide.util.Exceptions; 26 | import org.w3c.dom.Document; 27 | import org.w3c.dom.Node; 28 | import org.w3c.dom.NodeList; 29 | import org.xml.sax.InputSource; 30 | import org.xml.sax.SAXException; 31 | 32 | /** 33 | * 34 | * @author Michael Bien 35 | */ 36 | public class XPathEvaluator { 37 | 38 | private Transformer transformer; 39 | private DocumentBuilder docBuilder; 40 | private final XPathFactory xFac; 41 | 42 | public XPathEvaluator() { 43 | try { 44 | transformer = TransformerFactory.newInstance().newTransformer(); 45 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 46 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 47 | } catch (TransformerConfigurationException ex) { 48 | Exceptions.printStackTrace(ex); 49 | } 50 | try { 51 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 52 | factory.setNamespaceAware(true); 53 | factory.setValidating(false); 54 | docBuilder = factory.newDocumentBuilder(); 55 | docBuilder.setEntityResolver((String publicId, String systemId) -> new InputSource(new StringReader(""))); 56 | } catch (ParserConfigurationException ex) { 57 | Exceptions.printStackTrace(ex); 58 | } 59 | xFac = XPathFactory.newDefaultInstance(); 60 | } 61 | 62 | public String evalXPathToString(String xpath, String xml) throws SAXException, IOException, TransformerException, XPathExpressionException { 63 | 64 | if (docBuilder == null || transformer == null) { 65 | return ""; 66 | } 67 | 68 | NodeList resultXML = (NodeList) evaluate(xpath, xml, XPathConstants.NODESET); 69 | 70 | if (resultXML.getLength() > 0) { 71 | 72 | StringBuilder sb = new StringBuilder(resultXML.getLength()*80); 73 | 74 | for (int i = 0; i < resultXML.getLength(); i++) { 75 | 76 | Node item = resultXML.item(i); 77 | String nodeValue = item.getNodeValue(); 78 | if (nodeValue == null) { 79 | stripWhitespace(item); 80 | StringWriter writer = new StringWriter(128); 81 | transformer.transform(new DOMSource(item), new StreamResult(writer)); 82 | sb.append(writer.getBuffer()); 83 | } else { 84 | sb.append(nodeValue).append("\n"); 85 | } 86 | } 87 | return sb.toString(); 88 | } else { 89 | return ""; 90 | } 91 | } 92 | 93 | public Object evaluate(String xpath, String xml, QName ret) throws SAXException, IOException, XPathExpressionException { 94 | 95 | Document sourceXML = docBuilder.parse(new InputSource(new StringReader(xml))); 96 | 97 | XPath xPath = xFac.newXPath(); 98 | xPath.setNamespaceContext(new UniversalNamespaceResolver(sourceXML)); 99 | XPathExpression expr = xPath.compile(xpath); 100 | return expr.evaluate(sourceXML, ret); 101 | 102 | } 103 | 104 | public static void stripWhitespace(Node node) { 105 | NodeList children = node.getChildNodes(); 106 | for (int i = 0; i < children.getLength(); i++) { 107 | Node child = children.item(i); 108 | if (child.getNodeType() == Node.TEXT_NODE) { 109 | child.setTextContent(child.getTextContent().strip()); 110 | } 111 | stripWhitespace(child); 112 | } 113 | } 114 | 115 | private final static class UniversalNamespaceResolver implements NamespaceContext { 116 | 117 | private final Document sourceDocument; 118 | 119 | public UniversalNamespaceResolver(Document document) { 120 | sourceDocument = document; 121 | } 122 | 123 | @Override 124 | public String getNamespaceURI(String prefix) { 125 | if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) { 126 | return sourceDocument.lookupNamespaceURI(null); 127 | } else { 128 | return sourceDocument.lookupNamespaceURI(prefix); 129 | } 130 | } 131 | 132 | @Override 133 | public String getPrefix(String namespaceURI) { 134 | return sourceDocument.lookupPrefix(namespaceURI); 135 | } 136 | 137 | @Override 138 | public Iterator getPrefixes(String namespaceURI) { 139 | return null; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/ui/XPathEvaluatorThread.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.ui; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.InvocationTargetException; 5 | import javax.swing.SwingUtilities; 6 | import javax.swing.text.JTextComponent; 7 | import javax.xml.transform.TransformerException; 8 | import javax.xml.xpath.XPathExpressionException; 9 | import org.openide.util.Exceptions; 10 | import org.xml.sax.SAXException; 11 | import org.xml.sax.SAXParseException; 12 | 13 | final class XPathEvaluatorThread extends Thread { 14 | 15 | 16 | private volatile boolean updated = false; 17 | 18 | private final JTextComponent component; 19 | private final XPathEvaluator eval; 20 | 21 | private String xpath; 22 | private String xml; 23 | private String result; 24 | 25 | XPathEvaluatorThread(JTextComponent component) { 26 | 27 | super("XPath-evaluator"); 28 | 29 | this.component = component; 30 | this.setDaemon(true); 31 | 32 | this.eval = new XPathEvaluator(); 33 | 34 | } 35 | 36 | @Override 37 | public void run() { 38 | 39 | while (!Thread.interrupted()) { 40 | 41 | // don't sleep if values have changed 42 | if (!updated) { 43 | try { 44 | synchronized (this) { 45 | wait(); 46 | } 47 | } catch (InterruptedException ex) { 48 | Thread.interrupted(); 49 | return; 50 | } 51 | } 52 | 53 | try { 54 | updated = false; 55 | result = eval.evalXPathToString(xpath, xml); 56 | } catch (SAXParseException ex) { 57 | // return XML parser error messege if the document can't be parsed 58 | result = "can't parse document:\n ["+ex.getLineNumber()+", "+ex.getColumnNumber()+"] "+ex.getLocalizedMessage(); 59 | } catch (SAXException ex) { 60 | result = "can't parse document"; 61 | Exceptions.printStackTrace(ex); 62 | } catch (XPathExpressionException ex) { 63 | // return localized exception message on illegal xpath expr. 64 | result = "xpath expression error:\n "+ex.getCause().getLocalizedMessage(); 65 | } catch (IOException | TransformerException ex) { 66 | Exceptions.printStackTrace(ex); 67 | } 68 | /* 69 | * don't update if dirty (racecondition possible but doeasn't matter) 70 | */ 71 | if (updated && (result == null || result.isEmpty())) { 72 | continue; 73 | } 74 | try { 75 | SwingUtilities.invokeAndWait(() -> XPathEvaluatorThread.this.component.setText(result)); 76 | } catch (InterruptedException ex) { 77 | Thread.interrupted(); 78 | return; 79 | } catch (InvocationTargetException ex) { 80 | Exceptions.printStackTrace(ex); 81 | } 82 | } 83 | } 84 | 85 | 86 | /** 87 | * Starts asynchronous evaluation of the xpath expression. This method 88 | * can be called concurrently even if a evaluation is in progress. 89 | */ 90 | public synchronized void asyncEval(String xpath, String xml) { 91 | this.updated = true; 92 | this.xml = xml; 93 | this.xpath = xpath; 94 | if (isAlive()) { 95 | notify(); 96 | } else { 97 | start(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/ui/XPathTopComponent.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 | -------------------------------------------------------------------------------- /src/main/java/dev/mbien/xpathutil/ui/XPathTopComponent.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.ui; 2 | 3 | import dev.mbien.xpathutil.XPathDataObject; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.KeyAdapter; 6 | import java.awt.event.KeyEvent; 7 | import java.beans.PropertyChangeEvent; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.file.FileAlreadyExistsException; 11 | import java.nio.file.Files; 12 | import java.nio.file.StandardOpenOption; 13 | import java.util.logging.Level; 14 | import java.util.logging.Logger; 15 | import javax.swing.GroupLayout; 16 | import javax.swing.GroupLayout.Alignment; 17 | import javax.swing.JButton; 18 | import javax.swing.JComponent; 19 | import javax.swing.JEditorPane; 20 | import javax.swing.JFileChooser; 21 | import javax.swing.JLabel; 22 | import javax.swing.JOptionPane; 23 | import javax.swing.JScrollPane; 24 | import javax.swing.LayoutStyle.ComponentPlacement; 25 | import javax.swing.event.DocumentEvent; 26 | import javax.swing.event.DocumentListener; 27 | import javax.swing.filechooser.FileNameExtensionFilter; 28 | import javax.swing.text.BadLocationException; 29 | import javax.swing.text.Document; 30 | import javax.swing.text.JTextComponent; 31 | import org.netbeans.api.editor.EditorRegistry; 32 | import org.netbeans.editor.Utilities; 33 | import org.netbeans.modules.editor.NbEditorUtilities; 34 | import org.openide.awt.ActionID; 35 | import org.openide.awt.ActionReference; 36 | import org.openide.awt.Mnemonics; 37 | import org.openide.cookies.EditCookie; 38 | import org.openide.cookies.EditorCookie; 39 | import org.openide.cookies.OpenCookie; 40 | import org.openide.filesystems.FileObject; 41 | import org.openide.filesystems.FileUtil; 42 | import org.openide.loaders.DataObject; 43 | import org.openide.loaders.DataObjectNotFoundException; 44 | import org.openide.text.CloneableEditorSupport; 45 | import org.openide.util.Exceptions; 46 | import org.openide.util.ImageUtilities; 47 | import org.openide.util.NbBundle; 48 | import org.openide.windows.TopComponent; 49 | import org.openide.windows.WindowManager; 50 | 51 | /** 52 | * Top component for XPath expression evaluation. 53 | * @author Michael Bien 54 | */ 55 | @TopComponent.Description( 56 | preferredID = XPathTopComponent.PREFERRED_ID, 57 | iconBase="dev/mbien/xpathutil/ui/utilities-terminal.png", 58 | persistenceType = TopComponent.PERSISTENCE_ALWAYS 59 | ) 60 | @TopComponent.Registration( 61 | mode = "output", 62 | position = 999, 63 | openAtStartup = false 64 | ) 65 | @ActionID( 66 | category = "Window", 67 | id = "dev.mbien.xpathutil.XPathTopComponent" 68 | ) 69 | @ActionReference( 70 | path = "Menu/Window", 71 | position = 850 72 | ) 73 | @TopComponent.OpenActionRegistration( 74 | displayName = "XPath", 75 | preferredID = XPathTopComponent.PREFERRED_ID 76 | ) 77 | @NbBundle.Messages({ 78 | "CTL_XPathTopComponent=XPath", 79 | "HINT_XPathTopComponent=XPath evaluator", 80 | "XPathTopComponent.expressionLabel.text=Expression:", 81 | "XPathTopComponent.saveButton.text=Save..." 82 | }) 83 | public final class XPathTopComponent extends TopComponent { 84 | 85 | private static final long serialVersionUID = 1L; 86 | 87 | private static XPathTopComponent instance; 88 | 89 | /** path to the icon used by the component and its open action */ 90 | public static final String ICON_PATH = "dev/mbien/xpathutil/ui/utilities-terminal.png"; 91 | public static final String PREFERRED_ID = "XPathTopComponent"; 92 | 93 | private transient XPathEvaluatorThread evaluator; 94 | private transient final DocumentListener docListener; 95 | 96 | private transient DataObject activeDao; 97 | private transient String lastFilename; 98 | 99 | public XPathTopComponent() { 100 | 101 | initComponents(); 102 | 103 | setName(NbBundle.getMessage(XPathTopComponent.class, "CTL_XPathTopComponent")); 104 | setToolTipText(NbBundle.getMessage(XPathTopComponent.class, "HINT_XPathTopComponent")); 105 | setIcon(ImageUtilities.loadImage(ICON_PATH, true)); 106 | 107 | outputPane.setEditorKit(CloneableEditorSupport.getEditorKit("text/xml")); 108 | 109 | docListener = new DocumentListener() { 110 | @Override public void insertUpdate(DocumentEvent e) { xpathTextFieldChanged(e.getDocument()); } 111 | @Override public void removeUpdate(DocumentEvent e) { xpathTextFieldChanged(e.getDocument()); } 112 | @Override public void changedUpdate(DocumentEvent e) { xpathTextFieldChanged(e.getDocument()); } 113 | }; 114 | xpathTextField.getDocument().addDocumentListener(docListener); 115 | 116 | xpathTextField.addKeyListener(new KeyAdapter() { 117 | @Override public void keyPressed(KeyEvent e) { 118 | if (e.getKeyCode() == KeyEvent.VK_ENTER) { 119 | e.consume(); // TODO without this, completion items are inserted twice 120 | xpathTextFieldChanged(((JTextComponent)e.getComponent()).getDocument()); 121 | } 122 | } 123 | }); 124 | 125 | EditorRegistry.addPropertyChangeListener((PropertyChangeEvent evt) -> { 126 | if (EditorRegistry.FOCUS_GAINED_PROPERTY.equals(evt.getPropertyName()) && evt.getNewValue() instanceof JTextComponent) { 127 | editorFocusChanged((JTextComponent) evt.getNewValue()); 128 | } else if (EditorRegistry.COMPONENT_REMOVED_PROPERTY.equals(evt.getPropertyName())) { 129 | DataObject removed = NbEditorUtilities.getDataObject(((JTextComponent) evt.getOldValue()).getDocument()); 130 | if (activeDao == removed) { 131 | activeDao = null; 132 | } 133 | } 134 | }); 135 | 136 | } 137 | 138 | // //GEN-BEGIN:initComponents 139 | private void initComponents() { 140 | 141 | JLabel expressionLabel = new JLabel(); 142 | JScrollPane scrollPane = new JScrollPane(); 143 | outputPane = new JEditorPane(); 144 | JButton saveButton = new JButton(); 145 | JScrollPane editorScrollPane = createXPathEditor(); 146 | 147 | Mnemonics.setLocalizedText(expressionLabel, NbBundle.getMessage(XPathTopComponent.class, "XPathTopComponent.expressionLabel.text")); // NOI18N 148 | 149 | outputPane.setEditable(false); 150 | outputPane.setFocusable(false); 151 | scrollPane.setViewportView(outputPane); 152 | 153 | Mnemonics.setLocalizedText(saveButton, NbBundle.getMessage(XPathTopComponent.class, "XPathTopComponent.saveButton.text")); // NOI18N 154 | saveButton.addActionListener(this::saveButtonActionPerformed); 155 | 156 | GroupLayout layout = new GroupLayout(this); 157 | this.setLayout(layout); 158 | layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) 159 | .addGroup(Alignment.TRAILING, layout.createSequentialGroup() 160 | .addContainerGap() 161 | .addGroup(layout.createParallelGroup(Alignment.TRAILING) 162 | .addComponent(scrollPane, Alignment.LEADING) 163 | .addGroup(layout.createSequentialGroup() 164 | .addComponent(expressionLabel) 165 | .addPreferredGap(ComponentPlacement.RELATED) 166 | .addComponent(editorScrollPane, GroupLayout.DEFAULT_SIZE, 123, Short.MAX_VALUE) 167 | .addPreferredGap(ComponentPlacement.RELATED) 168 | .addComponent(saveButton))) 169 | .addContainerGap()) 170 | ); 171 | layout.setVerticalGroup(layout.createParallelGroup(Alignment.LEADING) 172 | .addGroup(layout.createSequentialGroup() 173 | .addContainerGap() 174 | .addGroup(layout.createParallelGroup(Alignment.LEADING, false) 175 | .addGroup(layout.createParallelGroup(Alignment.BASELINE) 176 | .addComponent(expressionLabel) 177 | .addComponent(saveButton)) 178 | .addComponent(editorScrollPane)) 179 | .addPreferredGap(ComponentPlacement.RELATED) 180 | .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE) 181 | .addContainerGap()) 182 | ); 183 | }// //GEN-END:initComponents 184 | 185 | private void saveButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed 186 | 187 | JFileChooser chooser = new JFileChooser(); 188 | 189 | chooser.setDialogTitle("Export As..."); 190 | chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 191 | chooser.setDialogType(JFileChooser.SAVE_DIALOG); 192 | chooser.setFileFilter(new FileNameExtensionFilter("XML files", "xml", "xsd", "xls", "html")); 193 | 194 | if (JFileChooser.APPROVE_OPTION == chooser.showSaveDialog(null)) { 195 | 196 | File file = chooser.getSelectedFile(); 197 | 198 | if(file != null) { 199 | 200 | try { 201 | try { 202 | Files.writeString(file.toPath(), outputPane.getText(), StandardOpenOption.CREATE_NEW); 203 | } catch (FileAlreadyExistsException ex) { 204 | if (JOptionPane.showConfirmDialog(null, "Overwrite existing file?") == JOptionPane.YES_OPTION) { 205 | Files.writeString(file.toPath(), outputPane.getText(), StandardOpenOption.TRUNCATE_EXISTING); 206 | } else { 207 | return; 208 | } 209 | } 210 | } catch (IOException ex) { 211 | Exceptions.printStackTrace(ex); 212 | } 213 | 214 | try{ 215 | DataObject dao = DataObject.find(FileUtil.toFileObject(file)); 216 | OpenCookie oc = dao.getLookup().lookup(OpenCookie.class); 217 | if(oc != null) { 218 | oc.open(); 219 | }else{ 220 | EditCookie ec = dao.getLookup().lookup(EditCookie.class); 221 | if(ec != null) { 222 | ec.edit(); 223 | } 224 | } 225 | }catch(DataObjectNotFoundException ex) { 226 | Logger.getLogger(this.getClass().getName()).log( 227 | Level.WARNING, "could not open exported xml file in editor", ex); 228 | } 229 | } 230 | } 231 | }//GEN-LAST:event_saveButtonActionPerformed 232 | 233 | 234 | // Variables declaration - do not modify//GEN-BEGIN:variables 235 | private JEditorPane outputPane; 236 | // End of variables declaration//GEN-END:variables 237 | private JEditorPane xpathTextField; 238 | 239 | private JScrollPane createXPathEditor() { 240 | JComponent[] comp = Utilities.createSingleLineEditor(XPathDataObject.MIME_TYPE); 241 | xpathTextField = (JEditorPane) comp[1]; 242 | return (JScrollPane) comp[0]; 243 | } 244 | 245 | private void editorFocusChanged(JTextComponent inFocus) { 246 | if (inFocus != null && xpathTextField != inFocus && !inFocus.getClass().getPackageName().startsWith("org.netbeans.modules.quicksearch")) { 247 | activeDao = NbEditorUtilities.getDataObject(inFocus.getDocument()); 248 | DataObject dao = activeDao; 249 | if (dao != null) { 250 | FileObject file = dao.getPrimaryFile(); 251 | if (file != null) { 252 | lastFilename = file.getNameExt(); 253 | return; 254 | } 255 | } 256 | lastFilename = "unknown"; 257 | } 258 | } 259 | 260 | private void xpathTextFieldChanged(Document doc) { 261 | try { 262 | if (activeDao != null) { 263 | String editorContent = getSourceEditorText(); 264 | 265 | if (!editorContent.isBlank()) { 266 | setDisplayName("XPath ["+lastFilename+"]"); 267 | evaluator.asyncEval(doc.getText(0, doc.getLength()), editorContent); 268 | return; 269 | } 270 | } 271 | setDisplayName("XPath"); 272 | outputPane.setText("focus xml document"); 273 | } catch (BadLocationException ignored) {} 274 | } 275 | 276 | public String getSourceEditorText() { 277 | if (activeDao == null) { 278 | return ""; 279 | } 280 | Document doc = activeDao.getLookup().lookup(EditorCookie.class).getDocument(); 281 | if (doc == null) { 282 | return ""; 283 | } 284 | try { 285 | return doc.getText(0, doc.getLength()); 286 | } catch (BadLocationException ignored) {} 287 | return ""; 288 | } 289 | 290 | public static synchronized XPathTopComponent findInstance() { 291 | TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID); 292 | if (win instanceof XPathTopComponent tc) { 293 | return tc; 294 | } 295 | Logger.getLogger(XPathTopComponent.class.getName()).warning( 296 | "There seem to be multiple components with the '" + PREFERRED_ID +"' ID!"); 297 | if (instance == null) { 298 | instance = new XPathTopComponent(); 299 | } 300 | return instance; 301 | } 302 | 303 | @Override 304 | public void componentOpened() { 305 | setDisplayName("XPath"); 306 | if (evaluator == null) { 307 | evaluator = new XPathEvaluatorThread(outputPane); 308 | } 309 | // xxx: this fixes disappearing auto completion after the window was closed 310 | // setting editor kit replaces the document too 311 | xpathTextField.getDocument().removeDocumentListener(docListener); 312 | xpathTextField.setEditorKit(CloneableEditorSupport.getEditorKit(XPathDataObject.MIME_TYPE)); 313 | xpathTextField.getDocument().addDocumentListener(docListener); 314 | } 315 | 316 | @Override 317 | public void componentClosed() { 318 | if (evaluator != null) { 319 | evaluator.interrupt(); 320 | evaluator = null; 321 | } 322 | activeDao = null; 323 | } 324 | 325 | public void requestActive(DataObject dao) { 326 | activeDao = dao; 327 | super.requestActive(); 328 | xpathTextField.requestFocusInWindow(); 329 | } 330 | 331 | } 332 | -------------------------------------------------------------------------------- /src/main/nbm/manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | OpenIDE-Module: dev.mbien.xpathutil 3 | OpenIDE-Module-Localizing-Bundle: dev/mbien/xpathutil/Bundle.properties 4 | OpenIDE-Module-Requires: org.openide.windows.WindowManager 5 | OpenIDE-Module-Specification-Version: 1.6 6 | OpenIDE-Module-Java-Dependencies: Java > 17 7 | -------------------------------------------------------------------------------- /src/main/resources/dev/mbien/xpathutil/Bundle.properties: -------------------------------------------------------------------------------- 1 | OpenIDE-Module-Display-Category=XML 2 | OpenIDE-Module-Long-Description=\ 3 | Utility providing XPath search functionality.\ 4 |

open with: Window -> XPath

\ 5 |

website: https://github.com/mbien/xpath-util

\ 6 |

author: Michael Bien https://mbien.dev

7 | OpenIDE-Module-Name=XPathUtil 8 | OpenIDE-Module-Short-Description=Provides XPath search functionality. 9 | -------------------------------------------------------------------------------- /src/main/resources/dev/mbien/xpathutil/ui/attribute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbien/xpath-util/199b933aa823315d7aee43691db0114bf2381f01/src/main/resources/dev/mbien/xpathutil/ui/attribute.png -------------------------------------------------------------------------------- /src/main/resources/dev/mbien/xpathutil/ui/element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbien/xpath-util/199b933aa823315d7aee43691db0114bf2381f01/src/main/resources/dev/mbien/xpathutil/ui/element.png -------------------------------------------------------------------------------- /src/main/resources/dev/mbien/xpathutil/ui/utilities-terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbien/xpath-util/199b933aa823315d7aee43691db0114bf2381f01/src/main/resources/dev/mbien/xpathutil/ui/utilities-terminal.png -------------------------------------------------------------------------------- /src/test/java/dev/mbien/xpathutil/ui/XPathEvaluatorTest.java: -------------------------------------------------------------------------------- 1 | package dev.mbien.xpathutil.ui; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | /** 8 | * 9 | * @author mbien 10 | */ 11 | public class XPathEvaluatorTest { 12 | 13 | private static final String XML_SIMPLE = """ 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
ApplesBananas
23 | 24 | 25 | 26 | Coffee Table 27 | 80 28 | 120 29 | 60 30 |
31 | 32 | 33 | true 34 | false 35 |
36 | 37 |
38 | """; 39 | 40 | private static final String XML_NAMESPACE_1 = """ 41 | 42 | 44 | 45 | 46 | 47 | Apples 48 | Bananas 49 | 50 | 51 | 52 | 53 | Coffee Table 54 | 80 55 | 56 | 57 | 58 | Hash Table 59 | 512 60 | 61 | 62 | 63 | """; 64 | 65 | 66 | private static final String XML_NAMESPACE_2 = """ 67 | 68 | 69 | 4.0.0 70 | dev.mbien 71 | mavenproject2 72 | 0.1-SNAPSHOT 73 | jar 74 | 75 | UTF-8 76 | 18 77 | 18 78 | dev.mbien.mavenproject2.Mavenproject2 79 | 80 | 81 | 82 | 83 | maven-compiler-plugin 84 | 3.10.1 85 | 86 | 87 | maven-surefire-plugin 88 | 3.0.0-M5 89 | 90 | 91 | 92 | 93 | """; 94 | 95 | 96 | @Test 97 | public void evalXPathSimple() throws Exception { 98 | 99 | XPathEvaluator eval = new XPathEvaluator(); 100 | 101 | assertEquals("Coffee Table", eval.evalXPathToString("/root/table/name", XML_SIMPLE).strip()); 102 | assertEquals("Coffee Table", eval.evalXPathToString("//name", XML_SIMPLE).strip()); 103 | assertEquals("rudolf", eval.evalXPathToString("//@name", XML_SIMPLE).strip()); 104 | assertEquals("rudolf", eval.evalXPathToString("/root/table[@name]/@name", XML_SIMPLE).strip()); 105 | } 106 | 107 | @Test 108 | public void evalXPathNameSpace1() throws Exception { 109 | 110 | XPathEvaluator eval = new XPathEvaluator(); 111 | 112 | assertEquals("Hash Table", eval.evalXPathToString("//f:table[f:size>100]/f:name/text()", XML_NAMESPACE_1).strip()); 113 | assertEquals(""" 114 | Apples 115 | Bananas""", 116 | eval.evalXPathToString("/h:root/h:table/h:tr/h:td/text()", XML_NAMESPACE_1).strip()); 117 | } 118 | 119 | @Test 120 | public void evalXPathNameSpace2() throws Exception { 121 | 122 | XPathEvaluator eval = new XPathEvaluator(); 123 | 124 | assertEquals("18", eval.evalXPathToString("/:project/:properties/:maven.compiler.source/text()", XML_NAMESPACE_2).strip()); 125 | assertEquals(""" 126 | maven-compiler-plugin 127 | maven-surefire-plugin""", 128 | eval.evalXPathToString("//:plugin/:artifactId/text()", XML_NAMESPACE_2).strip()); 129 | } 130 | 131 | 132 | } 133 | -------------------------------------------------------------------------------- /xpath-util.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbien/xpath-util/199b933aa823315d7aee43691db0114bf2381f01/xpath-util.png --------------------------------------------------------------------------------