// ĠCopyright Ġ 2 0 0 0 - 2 0 2 0 ĠJetBrains Ġs . r . o . ĠUse Ġof Ġthis Ġsource Ġcode Ġis Ġgoverned Ġby Ġthe ĠApache Ġ 2 . 0 Ġlicense Ġthat Ġcan Ġbe Ġfound Ġin Ġthe ĠLICENSE Ġfile . Ċ package Ġorg . jetbrains . intellij . build . io Ċ Ċ import Ġjava . nio . ByteBuffer Ċ import Ġjava . nio . file . Files Ċ import Ġjava . nio . file . Not Directory Exception Ċ import Ġjava . nio . file . Path Ċ import Ġjava . nio . file . Standard Open Option Ċ import Ġjava . time . Duration Ċ import Ġjava . util .* Ċ import Ġjava . util . concurrent . Executors Ċ import Ġjava . util . zip . Zip Entry Ċ import Ġkotlin . math . min Ċ Ċ // Ġsymlink s Ġnot Ġsupported Ġbut Ġcan Ġbe Ġeasily Ġimplemented Ġ- Ġsee ĠCollect ing Visitor . visit File Ċ fun Ġzip ( target File : ĠPath , Ġdirs : ĠMap < Path , ĠString >, Ġcompress : ĠBoolean Ġ= Ġtrue , Ġadd Dir Entries : ĠBoolean Ġ= Ġfalse , Ġlogger : ĠSystem . Logger ? Ġ= Ġnull ) Ġ{ ĊĠ Ġ// Ġnote Ġ- Ġdirs Ġcontain Ġduplicated Ġdirectories Ġ( you Ġcannot Ġsimply Ġadd Ġdirectory Ġentry Ġon Ġvisit Ġ- Ġuni queness Ġmust Ġbe Ġpreserved ) ĊĠ Ġ// Ġanyway , Ġdirectory Ġentry Ġare Ġnot Ġadded ĊĠ Ġval ĠexecutorService Ġ= ĠExec utors . new Work Ste aling Pool ( if Ġ( compress ) Ġmin ( Runtime . get Runtime (). available Processors () Ġ- Ġ 1 , Ġ 2 ) Ġelse Ġ 2 ) ĊĠ Ġval Ġzip Creator Ġ= ĠParallel Scatter Zip Creator ( executor Service Ġ= ĠexecutorService , Ġcompress Ġ= Ġcompress ) ĊĠ Ġval Ġarch iver Ġ= ĠZip Arch iver ( method Ġ= Ġif Ġ( compress ) ĠZip Entry . DE FL ATED Ġelse ĠZip Entry . STO RED , Ġzip Creator ) ĊĠ Ġfor Ġ(( dir , Ġprefix ) Ġin Ġdirs . entries ) Ġ{ ĊĠĠĠ Ġval Ġnormalized Dir Ġ= Ġdir . to AbsolutePath (). normalize () ĊĠĠĠ Ġarch iver . set Root Dir ( normalized Dir , Ġprefix ) ĊĠĠĠ Ġcompress Dir ( normalized Dir , Ġarch iver ) ĊĠ Ġ} ĊĠ ĠexecutorService . shutdown () ĊĊĠ ĠFiles . create Directories ( target File . parent ) ĊĠ ĠZip Archive OutputStream ( Files . new Byte Channel ( target File , ĠEnum Set . of ( Standard Open Option . WRITE , ĠStandard Open Option . CREATE ))) ĊĠĠĠ Ġ. use Ġ{ Ġout Ġ-> ĊĠĠĠĠĠ Ġzip Creator . writeTo ( out ) ĊĠĠĠĠĠ Ġif Ġ( add Dir Entries ) Ġ{ ĊĠĠĠĠĠĠĠ Ġadd Dir For Resource Files ( out ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠ Ġval Ġstats Ġ= Ġzip Creator . statistics Message ĊĠ Ġlogger ?. info ("${ target File . fileName } Ġcreated Ġin Ġ${ format Duration ( stats . compression Elapsed )} Ġ( merged Ġin Ġ${ format Duration ( stats . mer ging Elapsed )}) ") Ċ } Ċ Ċ private Ġfun Ġformat Duration ( value : ĠLong ): ĠString Ġ{ ĊĠ Ġreturn ĠDuration . of Millis ( value ). toString (). substring ( 2 ) ĊĠĠĠ Ġ. replace ( Regex ("( \\ d [ H MS ]) (? ! $ )"), Ġ"$ 1 Ġ") ĊĠĠĠ Ġ. toLowerCase () Ċ } Ċ Ċ private Ġval Ġempty ByteBuffer Ġ= ĠByteBuffer . allocate ( 0 ) Ċ Ċ private Ġfun Ġadd Dir For Resource Files ( out : ĠZip Archive OutputStream ) Ġ{ ĊĠ Ġval Ġdir Set Without Class Files Ġ= ĠHashSet < String >() ĊĠ Ġ@ Suppress (" D uplicated Code ") ĊĠ Ġfor Ġ( item Ġin Ġout . entries ) Ġ{ ĊĠĠĠ Ġval Ġname Ġ= Ġitem . entry . name ĊĠĠĠ Ġassert (! name . endsWith ('/ ')) ĊĠĠĠ Ġif Ġ( name . endsWith (". class ") Ġ|| Ġname . endsWith ("/ package . html ") Ġ|| Ġname Ġ== Ġ" META - INF / MAN IFEST . MF ") Ġ{ ĊĠĠĠĠĠ Ġcontinue ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġvar Ġslash Index Ġ= Ġname . lastIndexOf ('/') ĊĠĠĠ Ġif Ġ( slash Index Ġ!= Ġ- 1 ) Ġ{ ĊĠĠĠĠĠ Ġwhile Ġ( dir Set Without Class Files . add ( name . substring ( 0 , Ġslash Index ))) Ġ{ ĊĠĠĠĠĠĠĠ Ġslash Index Ġ= Ġname . lastIndexOf ('/', Ġslash Index Ġ- Ġ 2 ) ĊĠĠĠĠĠĠĠ Ġif Ġ( slash Index Ġ== Ġ- 1 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠ Ġbreak ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠ Ġ} ĊĊĠ Ġval Ġdirs Ġ= ĠArrayList ( dir Set Without Class Files ) ĊĠ Ġdirs . sort () ĊĠ Ġfor Ġ( dir Ġin Ġdirs ) Ġ{ ĊĠĠĠ Ġval Ġentry Ġ= ĠZip Entry ("$ dir /") ĊĠĠĠ Ġentry . method Ġ= ĠZip Entry . STO RED ĊĠĠĠ Ġentry . size Ġ= Ġ 0 ĊĠĠĠ Ġentry . compressed Size Ġ= Ġ 0 ĊĠĠĠ Ġout . add Raw Archive Entry ( entry , Ġempty ByteBuffer ) ĊĠ Ġ} Ċ } Ċ Ċ private Ġclass ĠZip Arch iver ( private Ġval Ġmethod : ĠInt , Ġval Ġzip Creator : ĠParallel Scatter Zip Creator ) Ġ{ ĊĠ Ġprivate Ġvar Ġlocal Prefix Length Ġ= Ġ- 1 ĊĠ Ġprivate Ġvar Ġarchive Prefix Ġ= Ġ"" ĊĊĠ Ġ// Ġroot Dir Ġmust Ġbe Ġabsolute Ġand Ġnormalized ĊĠ Ġfun Ġset Root Dir ( rootDir : ĠPath , Ġprefix : ĠString ) Ġ{ ĊĠĠĠ Ġarchive Prefix Ġ= Ġwhen Ġ{ ĊĠĠĠĠĠ Ġprefix . isNotEmpty () Ġ&& Ġ! prefix . endsWith ('/') Ġ-> Ġ"$ prefix /" ĊĠĠĠĠĠ Ġprefix Ġ== Ġ"/" Ġ-> Ġ"" ĊĠĠĠĠĠ Ġelse Ġ-> Ġprefix ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġlocal Prefix Length Ġ= Ġroot Dir . toString (). length Ġ+ Ġ 1 ĊĠ Ġ} ĊĊĠ Ġfun Ġadd File ( file : ĠPath ) Ġ{ ĊĠĠĠ Ġval Ġname Ġ= Ġarchive Prefix Ġ+ Ġfile . toString (). substring ( local Prefix Length ). replace ('\\ ', Ġ'/') ĊĠĠĠ Ġval Ġentry Ġ= ĠZip Entry ( name ) ĊĠĠĠ Ġentry . method Ġ= Ġmethod ĊĠĠĠ Ġzip Creator . add Entry ( entry , Ġfile ) ĊĠ Ġ} Ċ } Ċ Ċ private Ġfun Ġcompress Dir ( start Dir : ĠPath , Ġarch iver : ĠZip Arch iver ) Ġ{ ĊĠ Ġval Ġdir Candidates Ġ= ĠArray Deque < Path >() ĊĠ Ġdir Candidates . add ( start Dir ) ĊĠ Ġval Ġtemp List Ġ= ĠArrayList < Path >() ĊĠ Ġwhile Ġ( true ) Ġ{ ĊĠĠĠ Ġval Ġdir Ġ= Ġdir Candidates . poll First () Ġ?: Ġbreak ĊĠĠĠ Ġtemp List . clear () ĊĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠ ĠFiles . new Directory Stream ( dir ). use Ġ{ ĊĠĠĠĠĠĠĠ Ġtemp List . addAll ( it ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠĠĠ Ġcatch Ġ( e : ĠNot Directory Exception ) Ġ{ ĊĠĠĠĠĠ Ġ// Ġok , Ġit Ġis Ġfile ĊĠĠĠĠĠ Ġarch iver . add File ( dir ) ĊĠĠĠĠĠ Ġcontinue ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġtemp List . sort () ĊĠĠĠ Ġfor Ġ( file Ġin Ġtemp List ) Ġ{ ĊĠĠĠĠĠ Ġval Ġpath Ġ= Ġfile . toString () ĊĠĠĠĠĠ Ġif Ġ( path . endsWith (". class ") Ġ|| Ġ( path . length Ġ> Ġ 4 Ġ&& Ġpath [ path . length Ġ- Ġ 3 ] Ġ== Ġ'.' Ġ&& ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ( path . endsWith (" xml ") Ġ|| Ġpath . endsWith (" svg ") Ġ|| Ġpath . endsWith (" png "))) Ġ|| ĊĠĠĠĠĠĠĠĠĠ Ġpath . endsWith (". properties ")) Ġ{ ĊĠĠĠĠĠĠĠ Ġarch iver . add File ( file ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠĠĠ Ġelse Ġ{ ĊĠĠĠĠĠĠĠ Ġdir Candidates . add ( file ) ĊĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠ Ġ} Ċ }