add ZipFileSystem
This commit is contained in:
parent
24c0af8162
commit
b05b148361
|
@ -31,7 +31,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':FCLauncher')
|
implementation project(path: ':FCLauncher')
|
||||||
implementation 'com.github.marschall:zipfilesystem-standalone:1.0.1'
|
implementation project(path: ':ZipFileSystem')
|
||||||
implementation 'org.nanohttpd:nanohttpd:2.3.1'
|
implementation 'org.nanohttpd:nanohttpd:2.3.1'
|
||||||
implementation 'com.github.steveice10:opennbt:1.5'
|
implementation 'com.github.steveice10:opennbt:1.5'
|
||||||
implementation 'org.tukaani:xz:1.9'
|
implementation 'org.tukaani:xz:1.9'
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.github.marschall.ZipFileSystem'
|
||||||
|
compileSdk 34
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 26
|
||||||
|
targetSdk 34
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
fordebug {
|
||||||
|
initWith debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.CharsetDecoder;
|
||||||
|
import java.nio.charset.CharsetEncoder;
|
||||||
|
import java.nio.charset.CoderResult;
|
||||||
|
import java.nio.charset.CodingErrorAction;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for zipfile name and comment decoding and encoding
|
||||||
|
*
|
||||||
|
* @author Xueming Shen
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class ZipCoder {
|
||||||
|
|
||||||
|
String toString(byte[] ba, int length) {
|
||||||
|
CharsetDecoder cd = decoder().reset();
|
||||||
|
int len = (int)(length * cd.maxCharsPerByte());
|
||||||
|
char[] ca = new char[len];
|
||||||
|
if (len == 0)
|
||||||
|
return new String(ca);
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
|
||||||
|
CharBuffer cb = CharBuffer.wrap(ca);
|
||||||
|
CoderResult cr = cd.decode(bb, cb, true);
|
||||||
|
if (!cr.isUnderflow())
|
||||||
|
throw new IllegalArgumentException(cr.toString());
|
||||||
|
cr = cd.flush(cb);
|
||||||
|
if (!cr.isUnderflow())
|
||||||
|
throw new IllegalArgumentException(cr.toString());
|
||||||
|
return new String(ca, 0, cb.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
String toString(byte[] ba) {
|
||||||
|
return toString(ba, ba.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getBytes(String s) {
|
||||||
|
CharsetEncoder ce = encoder().reset();
|
||||||
|
char[] ca = s.toCharArray();
|
||||||
|
int len = (int)(ca.length * ce.maxBytesPerChar());
|
||||||
|
byte[] ba = new byte[len];
|
||||||
|
if (len == 0)
|
||||||
|
return ba;
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(ba);
|
||||||
|
CharBuffer cb = CharBuffer.wrap(ca);
|
||||||
|
CoderResult cr = ce.encode(cb, bb, true);
|
||||||
|
if (!cr.isUnderflow())
|
||||||
|
throw new IllegalArgumentException(cr.toString());
|
||||||
|
cr = ce.flush(bb);
|
||||||
|
if (!cr.isUnderflow())
|
||||||
|
throw new IllegalArgumentException(cr.toString());
|
||||||
|
if (bb.position() == ba.length) // defensive copy?
|
||||||
|
return ba;
|
||||||
|
else
|
||||||
|
return Arrays.copyOf(ba, bb.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume invoked only if "this" is not utf8
|
||||||
|
byte[] getBytesUTF8(String s) {
|
||||||
|
if (isutf8)
|
||||||
|
return getBytes(s);
|
||||||
|
if (utf8 == null)
|
||||||
|
utf8 = new ZipCoder(Charset.forName("UTF-8"));
|
||||||
|
return utf8.getBytes(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toStringUTF8(byte[] ba, int len) {
|
||||||
|
if (isutf8)
|
||||||
|
return toString(ba, len);
|
||||||
|
if (utf8 == null)
|
||||||
|
utf8 = new ZipCoder(Charset.forName("UTF-8"));
|
||||||
|
return utf8.toString(ba, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isUTF8() {
|
||||||
|
return isutf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Charset cs;
|
||||||
|
private boolean isutf8;
|
||||||
|
private ZipCoder utf8;
|
||||||
|
|
||||||
|
private ZipCoder(Charset cs) {
|
||||||
|
this.cs = cs;
|
||||||
|
this.isutf8 = cs.name().equals("UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZipCoder get(Charset charset) {
|
||||||
|
return new ZipCoder(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZipCoder get(String csn) {
|
||||||
|
try {
|
||||||
|
return new ZipCoder(Charset.forName(csn));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
return new ZipCoder(Charset.defaultCharset());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
|
||||||
|
private final ThreadLocal<CharsetEncoder> encTL = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private CharsetDecoder decoder() {
|
||||||
|
CharsetDecoder dec = decTL.get();
|
||||||
|
if (dec == null) {
|
||||||
|
dec = cs.newDecoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPORT)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||||
|
decTL.set(dec);
|
||||||
|
}
|
||||||
|
return dec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharsetEncoder encoder() {
|
||||||
|
CharsetEncoder enc = encTL.get();
|
||||||
|
if (enc == null) {
|
||||||
|
enc = cs.newEncoder()
|
||||||
|
.onMalformedInput(CodingErrorAction.REPORT)
|
||||||
|
.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||||
|
encTL.set(enc);
|
||||||
|
}
|
||||||
|
return enc;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Xueming Shen
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ZipConstants {
|
||||||
|
/*
|
||||||
|
* Compression methods
|
||||||
|
*/
|
||||||
|
static final int METHOD_STORED = 0;
|
||||||
|
static final int METHOD_DEFLATED = 8;
|
||||||
|
static final int METHOD_DEFLATED64 = 9;
|
||||||
|
static final int METHOD_BZIP2 = 12;
|
||||||
|
static final int METHOD_LZMA = 14;
|
||||||
|
static final int METHOD_LZ77 = 19;
|
||||||
|
static final int METHOD_AES = 99;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General purpose big flag
|
||||||
|
*/
|
||||||
|
static final int FLAG_ENCRYPTED = 0x01;
|
||||||
|
static final int FLAG_DATADESCR = 0x08; // crc, size and csize in dd
|
||||||
|
static final int FLAG_EFS = 0x800; // If this bit is set the filename and
|
||||||
|
// comment fields for this file must be
|
||||||
|
// encoded using UTF-8.
|
||||||
|
/*
|
||||||
|
* Header signatures
|
||||||
|
*/
|
||||||
|
static long LOCSIG = 0x04034b50L; // "PK\003\004"
|
||||||
|
static long EXTSIG = 0x08074b50L; // "PK\007\008"
|
||||||
|
static long CENSIG = 0x02014b50L; // "PK\001\002"
|
||||||
|
static long ENDSIG = 0x06054b50L; // "PK\005\006"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Header sizes in bytes (including signatures)
|
||||||
|
*/
|
||||||
|
static final int LOCHDR = 30; // LOC header size
|
||||||
|
static final int EXTHDR = 16; // EXT header size
|
||||||
|
static final int CENHDR = 46; // CEN header size
|
||||||
|
static final int ENDHDR = 22; // END header size
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local file (LOC) header field offsets
|
||||||
|
*/
|
||||||
|
static final int LOCVER = 4; // version needed to extract
|
||||||
|
static final int LOCFLG = 6; // general purpose bit flag
|
||||||
|
static final int LOCHOW = 8; // compression method
|
||||||
|
static final int LOCTIM = 10; // modification time
|
||||||
|
static final int LOCCRC = 14; // uncompressed file crc-32 value
|
||||||
|
static final int LOCSIZ = 18; // compressed size
|
||||||
|
static final int LOCLEN = 22; // uncompressed size
|
||||||
|
static final int LOCNAM = 26; // filename length
|
||||||
|
static final int LOCEXT = 28; // extra field length
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra local (EXT) header field offsets
|
||||||
|
*/
|
||||||
|
static final int EXTCRC = 4; // uncompressed file crc-32 value
|
||||||
|
static final int EXTSIZ = 8; // compressed size
|
||||||
|
static final int EXTLEN = 12; // uncompressed size
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Central directory (CEN) header field offsets
|
||||||
|
*/
|
||||||
|
static final int CENVEM = 4; // version made by
|
||||||
|
static final int CENVER = 6; // version needed to extract
|
||||||
|
static final int CENFLG = 8; // encrypt, decrypt flags
|
||||||
|
static final int CENHOW = 10; // compression method
|
||||||
|
static final int CENTIM = 12; // modification time
|
||||||
|
static final int CENCRC = 16; // uncompressed file crc-32 value
|
||||||
|
static final int CENSIZ = 20; // compressed size
|
||||||
|
static final int CENLEN = 24; // uncompressed size
|
||||||
|
static final int CENNAM = 28; // filename length
|
||||||
|
static final int CENEXT = 30; // extra field length
|
||||||
|
static final int CENCOM = 32; // comment length
|
||||||
|
static final int CENDSK = 34; // disk number start
|
||||||
|
static final int CENATT = 36; // internal file attributes
|
||||||
|
static final int CENATX = 38; // external file attributes
|
||||||
|
static final int CENOFF = 42; // LOC header offset
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End of central directory (END) header field offsets
|
||||||
|
*/
|
||||||
|
static final int ENDSUB = 8; // number of entries on this disk
|
||||||
|
static final int ENDTOT = 10; // total number of entries
|
||||||
|
static final int ENDSIZ = 12; // central directory size in bytes
|
||||||
|
static final int ENDOFF = 16; // offset of first CEN header
|
||||||
|
static final int ENDCOM = 20; // zip file comment length
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ZIP64 constants
|
||||||
|
*/
|
||||||
|
static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
|
||||||
|
static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
|
||||||
|
static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
|
||||||
|
static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
|
||||||
|
static final int ZIP64_EXTHDR = 24; // EXT header size
|
||||||
|
static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
|
||||||
|
|
||||||
|
static final int ZIP64_MINVAL32 = 0xFFFF;
|
||||||
|
static final long ZIP64_MINVAL = 0xFFFFFFFFL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zip64 End of central directory (END) header field offsets
|
||||||
|
*/
|
||||||
|
static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
|
||||||
|
static final int ZIP64_ENDVEM = 12; // version made by
|
||||||
|
static final int ZIP64_ENDVER = 14; // version needed to extract
|
||||||
|
static final int ZIP64_ENDNMD = 16; // number of this disk
|
||||||
|
static final int ZIP64_ENDDSK = 20; // disk number of start
|
||||||
|
static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
|
||||||
|
static final int ZIP64_ENDTOT = 32; // total number of entries
|
||||||
|
static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
|
||||||
|
static final int ZIP64_ENDOFF = 48; // offset of first CEN header
|
||||||
|
static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zip64 End of central directory locator field offsets
|
||||||
|
*/
|
||||||
|
static final int ZIP64_LOCDSK = 4; // disk number start
|
||||||
|
static final int ZIP64_LOCOFF = 8; // offset of zip64 end
|
||||||
|
static final int ZIP64_LOCTOT = 16; // total number of disks
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zip64 Extra local (EXT) header field offsets
|
||||||
|
*/
|
||||||
|
static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
|
||||||
|
static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
|
||||||
|
static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra field header ID
|
||||||
|
*/
|
||||||
|
static final int EXTID_ZIP64 = 0x0001; // ZIP64
|
||||||
|
static final int EXTID_NTFS = 0x000a; // NTFS
|
||||||
|
static final int EXTID_UNIX = 0x000d; // UNIX
|
||||||
|
static final int EXTID_EFS = 0x0017; // Strong Encryption
|
||||||
|
static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fields access methods
|
||||||
|
*/
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
static final int CH(byte[] b, int n) {
|
||||||
|
return toUnsignedInt(b[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int SH(byte[] b, int n) {
|
||||||
|
return toUnsignedInt(b[n]) | (toUnsignedInt(b[n + 1]) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int toUnsignedInt(byte x) {
|
||||||
|
return ((int) x) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final long LG(byte[] b, int n) {
|
||||||
|
return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final long LL(byte[] b, int n) {
|
||||||
|
return (LG(b, n)) | (LG(b, n + 4) << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final long GETSIG(byte[] b) {
|
||||||
|
return LG(b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// local file (LOC) header fields
|
||||||
|
static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
|
||||||
|
static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
|
||||||
|
static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags
|
||||||
|
static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method
|
||||||
|
static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time
|
||||||
|
static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data
|
||||||
|
static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size
|
||||||
|
static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size
|
||||||
|
static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length
|
||||||
|
static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length
|
||||||
|
|
||||||
|
// extra local (EXT) header fields
|
||||||
|
static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data
|
||||||
|
static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size
|
||||||
|
static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size
|
||||||
|
|
||||||
|
// end of central directory header (END) fields
|
||||||
|
static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk
|
||||||
|
static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries
|
||||||
|
static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size
|
||||||
|
static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset
|
||||||
|
static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment
|
||||||
|
static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);}
|
||||||
|
|
||||||
|
// zip64 end of central directory recoder fields
|
||||||
|
static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk
|
||||||
|
static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries
|
||||||
|
static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size
|
||||||
|
static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset
|
||||||
|
static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset
|
||||||
|
|
||||||
|
// central directory header (CEN) fields
|
||||||
|
static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); }
|
||||||
|
static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); }
|
||||||
|
static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); }
|
||||||
|
static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); }
|
||||||
|
static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);}
|
||||||
|
static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);}
|
||||||
|
static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);}
|
||||||
|
static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);}
|
||||||
|
static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);}
|
||||||
|
static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);}
|
||||||
|
static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);}
|
||||||
|
static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);}
|
||||||
|
static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);}
|
||||||
|
static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);}
|
||||||
|
static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);}
|
||||||
|
static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);}
|
||||||
|
|
||||||
|
/* The END header is followed by a variable length comment of size < 64k. */
|
||||||
|
static final long END_MAXLEN = 0xFFFF + ENDHDR;
|
||||||
|
static final int READBLOCKSZ = 128;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.ClosedDirectoryStreamException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.NotDirectoryException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipDirectoryStream implements DirectoryStream<Path> {
|
||||||
|
|
||||||
|
private final ZipFileSystem zipfs;
|
||||||
|
private final byte[] path;
|
||||||
|
private final DirectoryStream.Filter<? super Path> filter;
|
||||||
|
private volatile boolean isClosed;
|
||||||
|
private volatile Iterator<Path> itr;
|
||||||
|
|
||||||
|
ZipDirectoryStream(ZipPath zipPath,
|
||||||
|
DirectoryStream.Filter<? super java.nio.file.Path> filter)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
this.zipfs = zipPath.getFileSystem();
|
||||||
|
this.path = zipPath.getResolvedPath();
|
||||||
|
this.filter = filter;
|
||||||
|
// sanity check
|
||||||
|
if (!zipfs.isDirectory(path))
|
||||||
|
throw new NotDirectoryException(zipPath.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Iterator<Path> iterator() {
|
||||||
|
if (isClosed)
|
||||||
|
throw new ClosedDirectoryStreamException();
|
||||||
|
if (itr != null)
|
||||||
|
throw new IllegalStateException("Iterator has already been returned");
|
||||||
|
|
||||||
|
try {
|
||||||
|
itr = zipfs.iteratorOf(path, filter);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
return new Iterator<Path>() {
|
||||||
|
private Path next;
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (isClosed)
|
||||||
|
return false;
|
||||||
|
return itr.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Path next() {
|
||||||
|
if (isClosed)
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
return itr.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() throws IOException {
|
||||||
|
isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipFileAttributeView implements BasicFileAttributeView
|
||||||
|
{
|
||||||
|
private static enum AttrID {
|
||||||
|
size,
|
||||||
|
creationTime,
|
||||||
|
lastAccessTime,
|
||||||
|
lastModifiedTime,
|
||||||
|
isDirectory,
|
||||||
|
isRegularFile,
|
||||||
|
isSymbolicLink,
|
||||||
|
isOther,
|
||||||
|
fileKey,
|
||||||
|
compressedSize,
|
||||||
|
crc,
|
||||||
|
method
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ZipPath path;
|
||||||
|
private final boolean isZipView;
|
||||||
|
|
||||||
|
private ZipFileAttributeView(ZipPath path, boolean isZipView) {
|
||||||
|
this.path = path;
|
||||||
|
this.isZipView = isZipView;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V extends FileAttributeView> V get(ZipPath path, Class<V> type) {
|
||||||
|
if (type == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
if (type == BasicFileAttributeView.class)
|
||||||
|
return (V)new ZipFileAttributeView(path, false);
|
||||||
|
if (type == ZipFileAttributeView.class)
|
||||||
|
return (V)new ZipFileAttributeView(path, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZipFileAttributeView get(ZipPath path, String type) {
|
||||||
|
if (type == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
if (type.equals("basic"))
|
||||||
|
return new ZipFileAttributeView(path, false);
|
||||||
|
if (type.equals("zip"))
|
||||||
|
return new ZipFileAttributeView(path, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return isZipView ? "zip" : "basic";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZipFileAttributes readAttributes() throws IOException
|
||||||
|
{
|
||||||
|
return path.getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTimes(FileTime lastModifiedTime,
|
||||||
|
FileTime lastAccessTime,
|
||||||
|
FileTime createTime)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
path.setTimes(lastModifiedTime, lastAccessTime, createTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAttribute(String attribute, Object value)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime)
|
||||||
|
setTimes ((FileTime)value, null, null);
|
||||||
|
if (AttrID.valueOf(attribute) == AttrID.lastAccessTime)
|
||||||
|
setTimes (null, (FileTime)value, null);
|
||||||
|
if (AttrID.valueOf(attribute) == AttrID.creationTime)
|
||||||
|
setTimes (null, null, (FileTime)value);
|
||||||
|
return;
|
||||||
|
} catch (IllegalArgumentException x) {}
|
||||||
|
throw new UnsupportedOperationException("'" + attribute +
|
||||||
|
"' is unknown or read-only attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> readAttributes(String attributes)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
ZipFileAttributes zfas = readAttributes();
|
||||||
|
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
|
||||||
|
if ("*".equals(attributes)) {
|
||||||
|
for (AttrID id : AttrID.values()) {
|
||||||
|
try {
|
||||||
|
map.put(id.name(), attribute(id, zfas));
|
||||||
|
} catch (IllegalArgumentException x) {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String[] as = attributes.split(",");
|
||||||
|
for (String a : as) {
|
||||||
|
try {
|
||||||
|
map.put(a, attribute(AttrID.valueOf(a), zfas));
|
||||||
|
} catch (IllegalArgumentException x) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object attribute(AttrID id, ZipFileAttributes zfas) {
|
||||||
|
switch (id) {
|
||||||
|
case size:
|
||||||
|
return zfas.size();
|
||||||
|
case creationTime:
|
||||||
|
return zfas.creationTime();
|
||||||
|
case lastAccessTime:
|
||||||
|
return zfas.lastAccessTime();
|
||||||
|
case lastModifiedTime:
|
||||||
|
return zfas.lastModifiedTime();
|
||||||
|
case isDirectory:
|
||||||
|
return zfas.isDirectory();
|
||||||
|
case isRegularFile:
|
||||||
|
return zfas.isRegularFile();
|
||||||
|
case isSymbolicLink:
|
||||||
|
return zfas.isSymbolicLink();
|
||||||
|
case isOther:
|
||||||
|
return zfas.isOther();
|
||||||
|
case fileKey:
|
||||||
|
return zfas.fileKey();
|
||||||
|
case compressedSize:
|
||||||
|
if (isZipView)
|
||||||
|
return zfas.compressedSize();
|
||||||
|
break;
|
||||||
|
case crc:
|
||||||
|
if (isZipView)
|
||||||
|
return zfas.crc();
|
||||||
|
break;
|
||||||
|
case method:
|
||||||
|
if (isZipView)
|
||||||
|
return zfas.method();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Formatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipFileAttributes implements BasicFileAttributes
|
||||||
|
|
||||||
|
{
|
||||||
|
private final ZipFileSystem.Entry e;
|
||||||
|
|
||||||
|
ZipFileAttributes(ZipFileSystem.Entry e) {
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// basic attributes ///////////
|
||||||
|
@Override
|
||||||
|
public FileTime creationTime() {
|
||||||
|
if (e.ctime != -1)
|
||||||
|
return FileTime.fromMillis(e.ctime);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return e.isDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOther() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegularFile() {
|
||||||
|
return !e.isDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastAccessTime() {
|
||||||
|
if (e.atime != -1)
|
||||||
|
return FileTime.fromMillis(e.atime);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileTime lastModifiedTime() {
|
||||||
|
return FileTime.fromMillis(e.mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long size() {
|
||||||
|
return e.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSymbolicLink() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object fileKey() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// zip entry attributes ///////////
|
||||||
|
public long compressedSize() {
|
||||||
|
return e.csize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long crc() {
|
||||||
|
return e.crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int method() {
|
||||||
|
return e.method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] extra() {
|
||||||
|
if (e.extra != null)
|
||||||
|
return Arrays.copyOf(e.extra, e.extra.length);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] comment() {
|
||||||
|
if (e.comment != null)
|
||||||
|
return Arrays.copyOf(e.comment, e.comment.length);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder(1024);
|
||||||
|
Formatter fm = new Formatter(sb);
|
||||||
|
if (creationTime() != null)
|
||||||
|
fm.format(" creationTime : %tc%n", creationTime().toMillis());
|
||||||
|
else
|
||||||
|
fm.format(" creationTime : null%n");
|
||||||
|
|
||||||
|
if (lastAccessTime() != null)
|
||||||
|
fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
|
||||||
|
else
|
||||||
|
fm.format(" lastAccessTime : null%n");
|
||||||
|
fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
|
||||||
|
fm.format(" isRegularFile : %b%n", isRegularFile());
|
||||||
|
fm.format(" isDirectory : %b%n", isDirectory());
|
||||||
|
fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
|
||||||
|
fm.format(" isOther : %b%n", isOther());
|
||||||
|
fm.format(" fileKey : %s%n", fileKey());
|
||||||
|
fm.format(" size : %d%n", size());
|
||||||
|
fm.format(" compressedSize : %d%n", compressedSize());
|
||||||
|
fm.format(" crc : %x%n", crc());
|
||||||
|
fm.format(" method : %d%n", method());
|
||||||
|
fm.close();
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileStoreAttributeView;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipFileStore extends FileStore {
|
||||||
|
|
||||||
|
private final ZipFileSystem zfs;
|
||||||
|
|
||||||
|
ZipFileStore(ZipPath zpath) {
|
||||||
|
this.zfs = zpath.getFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return zfs.toString() + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String type() {
|
||||||
|
return "zipfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return zfs.isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
|
||||||
|
return (type == BasicFileAttributeView.class ||
|
||||||
|
type == ZipFileAttributeView.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsFileAttributeView(String name) {
|
||||||
|
return name.equals("basic") || name.equals("zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
|
||||||
|
if (type == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
return (V)null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTotalSpace() throws IOException {
|
||||||
|
return new ZipFileStoreAttributes(this).totalSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUsableSpace() throws IOException {
|
||||||
|
return new ZipFileStoreAttributes(this).usableSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUnallocatedSpace() throws IOException {
|
||||||
|
return new ZipFileStoreAttributes(this).unallocatedSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getAttribute(String attribute) throws IOException {
|
||||||
|
if (attribute.equals("totalSpace"))
|
||||||
|
return getTotalSpace();
|
||||||
|
if (attribute.equals("usableSpace"))
|
||||||
|
return getUsableSpace();
|
||||||
|
if (attribute.equals("unallocatedSpace"))
|
||||||
|
return getUnallocatedSpace();
|
||||||
|
throw new UnsupportedOperationException("does not support the given attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ZipFileStoreAttributes {
|
||||||
|
final FileStore fstore;
|
||||||
|
final long size;
|
||||||
|
|
||||||
|
public ZipFileStoreAttributes(ZipFileStore fileStore)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
Path path = FileSystems.getDefault().getPath(fileStore.name());
|
||||||
|
this.size = Files.size(path);
|
||||||
|
this.fstore = Files.getFileStore(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long totalSpace() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long usableSpace() throws IOException {
|
||||||
|
if (!fstore.isReadOnly())
|
||||||
|
return fstore.getUsableSpace();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long unallocatedSpace() throws IOException {
|
||||||
|
if (!fstore.isReadOnly())
|
||||||
|
return fstore.getUnallocatedSpace();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,346 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.channels.AsynchronousFileChannel;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.AccessMode;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.DirectoryStream.Filter;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystemAlreadyExistsException;
|
||||||
|
import java.nio.file.FileSystemNotFoundException;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.ProviderMismatchException;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.FileAttribute;
|
||||||
|
import java.nio.file.attribute.FileAttributeView;
|
||||||
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.zip.ZipError;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipFileSystemProvider extends FileSystemProvider {
|
||||||
|
|
||||||
|
|
||||||
|
private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
|
||||||
|
|
||||||
|
public ZipFileSystemProvider() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getScheme() {
|
||||||
|
return "zipfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Path uriToPath(URI uri) {
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
|
||||||
|
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// only support legacy JAR URL syntax jar:{uri}!/{entry} for now
|
||||||
|
String spec = uri.getRawSchemeSpecificPart();
|
||||||
|
int sep = spec.indexOf("!/");
|
||||||
|
if (sep != -1)
|
||||||
|
spec = spec.substring(0, sep);
|
||||||
|
return Paths.get(new URI(spec)).toAbsolutePath();
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ensureFile(Path path) {
|
||||||
|
try {
|
||||||
|
BasicFileAttributes attrs =
|
||||||
|
Files.readAttributes(path, BasicFileAttributes.class);
|
||||||
|
if (!attrs.isRegularFile())
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
return true;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
Path path = uriToPath(uri);
|
||||||
|
synchronized(filesystems) {
|
||||||
|
Path realPath = null;
|
||||||
|
if (ensureFile(path)) {
|
||||||
|
realPath = path.toRealPath();
|
||||||
|
if (filesystems.containsKey(realPath))
|
||||||
|
throw new FileSystemAlreadyExistsException();
|
||||||
|
}
|
||||||
|
ZipFileSystem zipfs = null;
|
||||||
|
try {
|
||||||
|
zipfs = new ZipFileSystem(this, path, env);
|
||||||
|
} catch (ZipError ze) {
|
||||||
|
String pname = path.toString();
|
||||||
|
if (pname.endsWith(".zip") || pname.endsWith(".jar"))
|
||||||
|
throw ze;
|
||||||
|
// assume NOT a zip/jar file
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
filesystems.put(realPath, zipfs);
|
||||||
|
return zipfs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileSystem newFileSystem(Path path, Map<String, ?> env)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (path.getFileSystem() != FileSystems.getDefault()) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
ensureFile(path);
|
||||||
|
try {
|
||||||
|
return new ZipFileSystem(this, path, env);
|
||||||
|
} catch (ZipError ze) {
|
||||||
|
String pname = path.toString();
|
||||||
|
if (pname.endsWith(".zip") || pname.endsWith(".jar"))
|
||||||
|
throw ze;
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getPath(URI uri) {
|
||||||
|
|
||||||
|
String spec = uri.getSchemeSpecificPart();
|
||||||
|
int sep = spec.indexOf("!/");
|
||||||
|
if (sep == -1)
|
||||||
|
throw new IllegalArgumentException("URI: "
|
||||||
|
+ uri
|
||||||
|
+ " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
|
||||||
|
return getFileSystem(uri).getPath(spec.substring(sep + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileSystem getFileSystem(URI uri) {
|
||||||
|
synchronized (filesystems) {
|
||||||
|
ZipFileSystem zipfs = null;
|
||||||
|
try {
|
||||||
|
zipfs = filesystems.get(uriToPath(uri).toRealPath());
|
||||||
|
} catch (IOException x) {
|
||||||
|
// ignore the ioe from toRealPath(), return FSNFE
|
||||||
|
}
|
||||||
|
if (zipfs == null)
|
||||||
|
throw new FileSystemNotFoundException();
|
||||||
|
return zipfs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks that the given file is a UnixPath
|
||||||
|
static final ZipPath toZipPath(Path path) {
|
||||||
|
if (path == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
if (!(path instanceof ZipPath))
|
||||||
|
throw new ProviderMismatchException();
|
||||||
|
return (ZipPath)path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkAccess(Path path, AccessMode... modes) throws IOException {
|
||||||
|
toZipPath(path).checkAccess(modes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copy(Path src, Path target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
toZipPath(src).copy(toZipPath(target), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createDirectory(Path path, FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
toZipPath(path).createDirectory(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void delete(Path path) throws IOException {
|
||||||
|
toZipPath(path).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V extends FileAttributeView> V
|
||||||
|
getFileAttributeView(Path path, Class<V> type, LinkOption... options)
|
||||||
|
{
|
||||||
|
return ZipFileAttributeView.get(toZipPath(path), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileStore getFileStore(Path path) throws IOException {
|
||||||
|
return toZipPath(path).getFileStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHidden(Path path) {
|
||||||
|
return toZipPath(path).isHidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSameFile(Path path, Path other) throws IOException {
|
||||||
|
return toZipPath(path).isSameFile(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void move(Path src, Path target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
toZipPath(src).move(toZipPath(target), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
|
||||||
|
Set<? extends OpenOption> options,
|
||||||
|
ExecutorService exec,
|
||||||
|
FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SeekableByteChannel newByteChannel(Path path,
|
||||||
|
Set<? extends OpenOption> options,
|
||||||
|
FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).newByteChannel(options, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DirectoryStream<Path> newDirectoryStream(
|
||||||
|
Path path, Filter<? super Path> filter) throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).newDirectoryStream(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileChannel newFileChannel(Path path,
|
||||||
|
Set<? extends OpenOption> options,
|
||||||
|
FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).newFileChannel(options, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream newInputStream(Path path, OpenOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).newInputStream(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream newOutputStream(Path path, OpenOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).newOutputStream(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <A extends BasicFileAttributes> A
|
||||||
|
readAttributes(Path path, Class<A> type, LinkOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (type == BasicFileAttributes.class || type == ZipFileAttributes.class)
|
||||||
|
return (A)toZipPath(path).getAttributes();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object>
|
||||||
|
readAttributes(Path path, String attribute, LinkOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return toZipPath(path).readAttributes(attribute, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path readSymbolicLink(Path link) throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(Path path, String attribute,
|
||||||
|
Object value, LinkOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
toZipPath(path).setAttribute(attribute, value, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException {
|
||||||
|
synchronized (filesystems) {
|
||||||
|
zfpath = zfpath.toRealPath();
|
||||||
|
if (filesystems.get(zfpath) == zfs)
|
||||||
|
filesystems.remove(zfpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,915 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
|
||||||
|
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||||
|
import static java.nio.file.StandardOpenOption.CREATE_NEW;
|
||||||
|
import static java.nio.file.StandardOpenOption.READ;
|
||||||
|
import static java.nio.file.StandardOpenOption.WRITE;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.AccessDeniedException;
|
||||||
|
import java.nio.file.AccessMode;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.DirectoryStream.Filter;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.ProviderMismatchException;
|
||||||
|
import java.nio.file.ReadOnlyFileSystemException;
|
||||||
|
import java.nio.file.WatchEvent;
|
||||||
|
import java.nio.file.WatchKey;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileAttribute;
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ZipPath implements Path {
|
||||||
|
|
||||||
|
private final ZipFileSystem zfs;
|
||||||
|
private final byte[] path;
|
||||||
|
private volatile int[] offsets;
|
||||||
|
private int hashcode = 0; // cached hashcode (created lazily)
|
||||||
|
|
||||||
|
ZipPath(ZipFileSystem zfs, byte[] path) {
|
||||||
|
this(zfs, path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipPath(ZipFileSystem zfs, byte[] path, boolean normalized)
|
||||||
|
{
|
||||||
|
this.zfs = zfs;
|
||||||
|
if (normalized)
|
||||||
|
this.path = path;
|
||||||
|
else
|
||||||
|
this.path = normalize(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath getRoot() {
|
||||||
|
if (this.isAbsolute())
|
||||||
|
return new ZipPath(zfs, new byte[]{path[0]});
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getFileName() {
|
||||||
|
initOffsets();
|
||||||
|
int count = offsets.length;
|
||||||
|
if (count == 0)
|
||||||
|
return null; // no elements so no name
|
||||||
|
if (count == 1 && path[0] != '/')
|
||||||
|
return this;
|
||||||
|
int lastOffset = offsets[count-1];
|
||||||
|
int len = path.length - lastOffset;
|
||||||
|
byte[] result = new byte[len];
|
||||||
|
System.arraycopy(path, lastOffset, result, 0, len);
|
||||||
|
return new ZipPath(zfs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath getParent() {
|
||||||
|
initOffsets();
|
||||||
|
int count = offsets.length;
|
||||||
|
if (count == 0) // no elements so no parent
|
||||||
|
return null;
|
||||||
|
int len = offsets[count-1] - 1;
|
||||||
|
if (len <= 0) // parent is root only (may be null)
|
||||||
|
return getRoot();
|
||||||
|
byte[] result = new byte[len];
|
||||||
|
System.arraycopy(path, 0, result, 0, len);
|
||||||
|
return new ZipPath(zfs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNameCount() {
|
||||||
|
initOffsets();
|
||||||
|
return offsets.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath getName(int index) {
|
||||||
|
initOffsets();
|
||||||
|
if (index < 0 || index >= offsets.length)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
int begin = offsets[index];
|
||||||
|
int len;
|
||||||
|
if (index == (offsets.length-1))
|
||||||
|
len = path.length - begin;
|
||||||
|
else
|
||||||
|
len = offsets[index+1] - begin - 1;
|
||||||
|
// construct result
|
||||||
|
byte[] result = new byte[len];
|
||||||
|
System.arraycopy(path, begin, result, 0, len);
|
||||||
|
return new ZipPath(zfs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath subpath(int beginIndex, int endIndex) {
|
||||||
|
initOffsets();
|
||||||
|
if (beginIndex < 0 ||
|
||||||
|
beginIndex >= offsets.length ||
|
||||||
|
endIndex > offsets.length ||
|
||||||
|
beginIndex >= endIndex)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
// starting offset and length
|
||||||
|
int begin = offsets[beginIndex];
|
||||||
|
int len;
|
||||||
|
if (endIndex == offsets.length)
|
||||||
|
len = path.length - begin;
|
||||||
|
else
|
||||||
|
len = offsets[endIndex] - begin - 1;
|
||||||
|
// construct result
|
||||||
|
byte[] result = new byte[len];
|
||||||
|
System.arraycopy(path, begin, result, 0, len);
|
||||||
|
return new ZipPath(zfs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath toRealPath(LinkOption... options) throws IOException {
|
||||||
|
ZipPath realPath = new ZipPath(zfs, getResolvedPath()).toAbsolutePath();
|
||||||
|
realPath.checkAccess();
|
||||||
|
return realPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHidden() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath toAbsolutePath() {
|
||||||
|
if (isAbsolute()) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
//add / bofore the existing path
|
||||||
|
byte[] defaultdir = zfs.getDefaultDir().path;
|
||||||
|
int defaultlen = defaultdir.length;
|
||||||
|
boolean endsWith = (defaultdir[defaultlen - 1] == '/');
|
||||||
|
byte[] t = null;
|
||||||
|
if (endsWith)
|
||||||
|
t = new byte[defaultlen + path.length];
|
||||||
|
else
|
||||||
|
t = new byte[defaultlen + 1 + path.length];
|
||||||
|
System.arraycopy(defaultdir, 0, t, 0, defaultlen);
|
||||||
|
if (!endsWith)
|
||||||
|
t[defaultlen++] = '/';
|
||||||
|
System.arraycopy(path, 0, t, defaultlen, path.length);
|
||||||
|
return new ZipPath(zfs, t, true); // normalized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI toUri() {
|
||||||
|
try {
|
||||||
|
return new URI(zfs.provider().getScheme(),
|
||||||
|
zfs.getZipFile().toUri() +
|
||||||
|
"!" +
|
||||||
|
zfs.getString(toAbsolutePath().path),
|
||||||
|
null);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equalsNameAt(ZipPath other, int index) {
|
||||||
|
int mbegin = offsets[index];
|
||||||
|
int mlen = 0;
|
||||||
|
if (index == (offsets.length-1))
|
||||||
|
mlen = path.length - mbegin;
|
||||||
|
else
|
||||||
|
mlen = offsets[index + 1] - mbegin - 1;
|
||||||
|
int obegin = other.offsets[index];
|
||||||
|
int olen = 0;
|
||||||
|
if (index == (other.offsets.length - 1))
|
||||||
|
olen = other.path.length - obegin;
|
||||||
|
else
|
||||||
|
olen = other.offsets[index + 1] - obegin - 1;
|
||||||
|
if (mlen != olen)
|
||||||
|
return false;
|
||||||
|
int n = 0;
|
||||||
|
while(n < mlen) {
|
||||||
|
if (path[mbegin + n] != other.path[obegin + n])
|
||||||
|
return false;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path relativize(Path other) {
|
||||||
|
final ZipPath o = checkPath(other);
|
||||||
|
if (o.equals(this))
|
||||||
|
return new ZipPath(getFileSystem(), new byte[0], true);
|
||||||
|
if (/* this.getFileSystem() != o.getFileSystem() || */
|
||||||
|
this.isAbsolute() != o.isAbsolute()) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
int mc = this.getNameCount();
|
||||||
|
int oc = o.getNameCount();
|
||||||
|
int n = Math.min(mc, oc);
|
||||||
|
int i = 0;
|
||||||
|
while (i < n) {
|
||||||
|
if (!equalsNameAt(o, i))
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
int dotdots = mc - i;
|
||||||
|
int len = dotdots * 3 - 1;
|
||||||
|
if (i < oc)
|
||||||
|
len += (o.path.length - o.offsets[i] + 1);
|
||||||
|
byte[] result = new byte[len];
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
while (dotdots > 0) {
|
||||||
|
result[pos++] = (byte)'.';
|
||||||
|
result[pos++] = (byte)'.';
|
||||||
|
if (pos < len) // no tailing slash at the end
|
||||||
|
result[pos++] = (byte)'/';
|
||||||
|
dotdots--;
|
||||||
|
}
|
||||||
|
if (i < oc)
|
||||||
|
System.arraycopy(o.path, o.offsets[i],
|
||||||
|
result, pos,
|
||||||
|
o.path.length - o.offsets[i]);
|
||||||
|
return new ZipPath(getFileSystem(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipFileSystem getFileSystem() {
|
||||||
|
return zfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAbsolute() {
|
||||||
|
return (this.path.length > 0 && path[0] == '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath resolve(Path other) {
|
||||||
|
final ZipPath o = checkPath(other);
|
||||||
|
if (o.isAbsolute())
|
||||||
|
return o;
|
||||||
|
byte[] resolved = null;
|
||||||
|
if (this.path[path.length - 1] == '/') {
|
||||||
|
resolved = new byte[path.length + o.path.length];
|
||||||
|
System.arraycopy(path, 0, resolved, 0, path.length);
|
||||||
|
System.arraycopy(o.path, 0, resolved, path.length, o.path.length);
|
||||||
|
} else {
|
||||||
|
resolved = new byte[path.length + 1 + o.path.length];
|
||||||
|
System.arraycopy(path, 0, resolved, 0, path.length);
|
||||||
|
resolved[path.length] = '/';
|
||||||
|
System.arraycopy(o.path, 0, resolved, path.length + 1, o.path.length);
|
||||||
|
}
|
||||||
|
return new ZipPath(zfs, resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path resolveSibling(Path other) {
|
||||||
|
if (other == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
Path parent = getParent();
|
||||||
|
return (parent == null) ? other : parent.resolve(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startsWith(Path other) {
|
||||||
|
final ZipPath o = checkPath(other);
|
||||||
|
if (o.isAbsolute() != this.isAbsolute() ||
|
||||||
|
o.path.length > this.path.length)
|
||||||
|
return false;
|
||||||
|
int olast = o.path.length;
|
||||||
|
for (int i = 0; i < olast; i++) {
|
||||||
|
if (o.path[i] != this.path[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
olast--;
|
||||||
|
return o.path.length == this.path.length ||
|
||||||
|
o.path[olast] == '/' ||
|
||||||
|
this.path[olast + 1] == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean endsWith(Path other) {
|
||||||
|
final ZipPath o = checkPath(other);
|
||||||
|
int olast = o.path.length - 1;
|
||||||
|
if (olast > 0 && o.path[olast] == '/')
|
||||||
|
olast--;
|
||||||
|
int last = this.path.length - 1;
|
||||||
|
if (last > 0 && this.path[last] == '/')
|
||||||
|
last--;
|
||||||
|
if (olast == -1) // o.path.length == 0
|
||||||
|
return last == -1;
|
||||||
|
if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) ||
|
||||||
|
(last < olast))
|
||||||
|
return false;
|
||||||
|
for (; olast >= 0; olast--, last--) {
|
||||||
|
if (o.path[olast] != this.path[last])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return o.path[olast + 1] == '/' ||
|
||||||
|
last == -1 || this.path[last] == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipPath resolve(String other) {
|
||||||
|
return resolve(getFileSystem().getPath(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Path resolveSibling(String other) {
|
||||||
|
return resolveSibling(getFileSystem().getPath(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean startsWith(String other) {
|
||||||
|
return startsWith(getFileSystem().getPath(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean endsWith(String other) {
|
||||||
|
return endsWith(getFileSystem().getPath(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path normalize() {
|
||||||
|
byte[] resolved = getResolved();
|
||||||
|
if (resolved == path) // no change
|
||||||
|
return this;
|
||||||
|
return new ZipPath(zfs, resolved, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZipPath checkPath(Path path) {
|
||||||
|
if (path == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
if (!(path instanceof ZipPath))
|
||||||
|
throw new ProviderMismatchException();
|
||||||
|
return (ZipPath) path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create offset list if not already created
|
||||||
|
private void initOffsets() {
|
||||||
|
if (offsets == null) {
|
||||||
|
int count, index;
|
||||||
|
// count names
|
||||||
|
count = 0;
|
||||||
|
index = 0;
|
||||||
|
while (index < path.length) {
|
||||||
|
byte c = path[index++];
|
||||||
|
if (c != '/') {
|
||||||
|
count++;
|
||||||
|
while (index < path.length && path[index] != '/')
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// populate offsets
|
||||||
|
int[] result = new int[count];
|
||||||
|
count = 0;
|
||||||
|
index = 0;
|
||||||
|
while (index < path.length) {
|
||||||
|
byte c = path[index];
|
||||||
|
if (c == '/') {
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
result[count++] = index++;
|
||||||
|
while (index < path.length && path[index] != '/')
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
if (offsets == null)
|
||||||
|
offsets = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolved path for locating zip entry inside the zip file,
|
||||||
|
// the result path does not contain ./ and .. components
|
||||||
|
private volatile byte[] resolved = null;
|
||||||
|
byte[] getResolvedPath() {
|
||||||
|
byte[] r = resolved;
|
||||||
|
if (r == null) {
|
||||||
|
if (isAbsolute())
|
||||||
|
r = getResolved();
|
||||||
|
else
|
||||||
|
r = toAbsolutePath().getResolvedPath();
|
||||||
|
if (r[0] == '/')
|
||||||
|
r = Arrays.copyOfRange(r, 1, r.length);
|
||||||
|
resolved = r;
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes redundant slashs, replace "\" to zip separator "/"
|
||||||
|
// and check for invalid characters
|
||||||
|
private byte[] normalize(byte[] path) {
|
||||||
|
if (path.length == 0)
|
||||||
|
return path;
|
||||||
|
byte prevC = 0;
|
||||||
|
for (int i = 0; i < path.length; i++) {
|
||||||
|
byte c = path[i];
|
||||||
|
if (c == '\\')
|
||||||
|
return normalize(path, i);
|
||||||
|
if (c == (byte)'/' && prevC == '/')
|
||||||
|
return normalize(path, i - 1);
|
||||||
|
if (c == '\u0000')
|
||||||
|
throw new InvalidPathException(zfs.getString(path),
|
||||||
|
"Path: nul character not allowed");
|
||||||
|
prevC = c;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] normalize(byte[] path, int off) {
|
||||||
|
byte[] to = new byte[path.length];
|
||||||
|
int n = 0;
|
||||||
|
while (n < off) {
|
||||||
|
to[n] = path[n];
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
int m = n;
|
||||||
|
byte prevC = 0;
|
||||||
|
while (n < path.length) {
|
||||||
|
byte c = path[n++];
|
||||||
|
if (c == (byte)'\\')
|
||||||
|
c = (byte)'/';
|
||||||
|
if (c == (byte)'/' && prevC == (byte)'/')
|
||||||
|
continue;
|
||||||
|
if (c == '\u0000')
|
||||||
|
throw new InvalidPathException(zfs.getString(path),
|
||||||
|
"Path: nul character not allowed");
|
||||||
|
to[m++] = c;
|
||||||
|
prevC = c;
|
||||||
|
}
|
||||||
|
if (m > 1 && to[m - 1] == '/')
|
||||||
|
m--;
|
||||||
|
return (m == to.length)? to : Arrays.copyOf(to, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove DotSlash(./) and resolve DotDot (..) components
|
||||||
|
private byte[] getResolved() {
|
||||||
|
if (path.length == 0)
|
||||||
|
return path;
|
||||||
|
for (int i = 0; i < path.length; i++) {
|
||||||
|
byte c = path[i];
|
||||||
|
if (c == (byte)'.')
|
||||||
|
return resolve0();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TBD: performance, avoid initOffsets
|
||||||
|
private byte[] resolve0() {
|
||||||
|
byte[] to = new byte[path.length];
|
||||||
|
int nc = getNameCount();
|
||||||
|
int[] lastM = new int[nc];
|
||||||
|
int lastMOff = -1;
|
||||||
|
int m = 0;
|
||||||
|
for (int i = 0; i < nc; i++) {
|
||||||
|
int n = offsets[i];
|
||||||
|
int len = (i == offsets.length - 1)?
|
||||||
|
(path.length - n):(offsets[i + 1] - n - 1);
|
||||||
|
if (len == 1 && path[n] == (byte)'.') {
|
||||||
|
if (m == 0 && path[0] == '/') // absolute path
|
||||||
|
to[m++] = '/';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (len == 2 && path[n] == '.' && path[n + 1] == '.') {
|
||||||
|
if (lastMOff >= 0) {
|
||||||
|
m = lastM[lastMOff--]; // retreat
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (path[0] == '/') { // "/../xyz" skip
|
||||||
|
if (m == 0)
|
||||||
|
to[m++] = '/';
|
||||||
|
} else { // "../xyz" -> "../xyz"
|
||||||
|
if (m != 0 && to[m-1] != '/')
|
||||||
|
to[m++] = '/';
|
||||||
|
while (len-- > 0)
|
||||||
|
to[m++] = path[n++];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (m == 0 && path[0] == '/' || // absolute path
|
||||||
|
m != 0 && to[m-1] != '/') { // not the first name
|
||||||
|
to[m++] = '/';
|
||||||
|
}
|
||||||
|
lastM[++lastMOff] = m;
|
||||||
|
while (len-- > 0)
|
||||||
|
to[m++] = path[n++];
|
||||||
|
}
|
||||||
|
if (m > 1 && to[m - 1] == '/')
|
||||||
|
m--;
|
||||||
|
return (m == to.length)? to : Arrays.copyOf(to, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return zfs.getString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int h = hashcode;
|
||||||
|
if (h == 0)
|
||||||
|
hashcode = h = Arrays.hashCode(path);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj != null &&
|
||||||
|
obj instanceof ZipPath &&
|
||||||
|
this.zfs == ((ZipPath)obj).zfs &&
|
||||||
|
compareTo((Path) obj) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Path other) {
|
||||||
|
final ZipPath o = checkPath(other);
|
||||||
|
int len1 = this.path.length;
|
||||||
|
int len2 = o.path.length;
|
||||||
|
|
||||||
|
int n = Math.min(len1, len2);
|
||||||
|
byte v1[] = this.path;
|
||||||
|
byte v2[] = o.path;
|
||||||
|
|
||||||
|
int k = 0;
|
||||||
|
while (k < n) {
|
||||||
|
int c1 = v1[k] & 0xff;
|
||||||
|
int c2 = v2[k] & 0xff;
|
||||||
|
if (c1 != c2)
|
||||||
|
return c1 - c2;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
return len1 - len2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WatchKey register(
|
||||||
|
WatchService watcher,
|
||||||
|
WatchEvent.Kind<?>[] events,
|
||||||
|
WatchEvent.Modifier... modifiers) {
|
||||||
|
if (watcher == null || events == null || modifiers == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
|
||||||
|
return register(watcher, events, new WatchEvent.Modifier[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final File toFile() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Path> iterator() {
|
||||||
|
return new Iterator<Path>() {
|
||||||
|
private int i = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return (i < getNameCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path next() {
|
||||||
|
if (i < getNameCount()) {
|
||||||
|
Path result = getName(i);
|
||||||
|
i++;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new ReadOnlyFileSystemException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
void createDirectory(FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
zfs.createDirectory(getResolvedPath(), attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream newInputStream(OpenOption... options) throws IOException
|
||||||
|
{
|
||||||
|
if (options.length > 0) {
|
||||||
|
for (OpenOption opt : options) {
|
||||||
|
if (opt != READ)
|
||||||
|
throw new UnsupportedOperationException("'" + opt + "' not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zfs.newInputStream(getResolvedPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return new ZipDirectoryStream(this, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete() throws IOException {
|
||||||
|
zfs.deleteFile(getResolvedPath(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteIfExists() throws IOException {
|
||||||
|
zfs.deleteFile(getResolvedPath(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFileAttributes getAttributes() throws IOException
|
||||||
|
{
|
||||||
|
ZipFileAttributes zfas = zfs.getFileAttributes(getResolvedPath());
|
||||||
|
if (zfas == null)
|
||||||
|
throw new NoSuchFileException(toString());
|
||||||
|
return zfas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAttribute(String attribute, Object value, LinkOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
String type = null;
|
||||||
|
String attr = null;
|
||||||
|
int colonPos = attribute.indexOf(':');
|
||||||
|
if (colonPos == -1) {
|
||||||
|
type = "basic";
|
||||||
|
attr = attribute;
|
||||||
|
} else {
|
||||||
|
type = attribute.substring(0, colonPos++);
|
||||||
|
attr = attribute.substring(colonPos);
|
||||||
|
}
|
||||||
|
ZipFileAttributeView view = ZipFileAttributeView.get(this, type);
|
||||||
|
if (view == null)
|
||||||
|
throw new UnsupportedOperationException("view <" + view + "> is not supported");
|
||||||
|
view.setAttribute(attr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
zfs.setTimes(getResolvedPath(), mtime, atime, ctime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> readAttributes(String attributes, LinkOption... options)
|
||||||
|
throws IOException
|
||||||
|
|
||||||
|
{
|
||||||
|
String view = null;
|
||||||
|
String attrs = null;
|
||||||
|
int colonPos = attributes.indexOf(':');
|
||||||
|
if (colonPos == -1) {
|
||||||
|
view = "basic";
|
||||||
|
attrs = attributes;
|
||||||
|
} else {
|
||||||
|
view = attributes.substring(0, colonPos++);
|
||||||
|
attrs = attributes.substring(colonPos);
|
||||||
|
}
|
||||||
|
ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view);
|
||||||
|
if (zfv == null) {
|
||||||
|
throw new UnsupportedOperationException("view not supported");
|
||||||
|
}
|
||||||
|
return zfv.readAttributes(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStore getFileStore() throws IOException {
|
||||||
|
// each ZipFileSystem only has one root (as requested for now)
|
||||||
|
if (exists())
|
||||||
|
return zfs.getFileStore(this);
|
||||||
|
throw new NoSuchFileException(zfs.getString(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSameFile(Path other) throws IOException {
|
||||||
|
if (this.equals(other))
|
||||||
|
return true;
|
||||||
|
if (other == null ||
|
||||||
|
this.getFileSystem() != other.getFileSystem())
|
||||||
|
return false;
|
||||||
|
this.checkAccess();
|
||||||
|
((ZipPath)other).checkAccess();
|
||||||
|
return Arrays.equals(this.getResolvedPath(),
|
||||||
|
((ZipPath)other).getResolvedPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
|
||||||
|
FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return zfs.newByteChannel(getResolvedPath(), options, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileChannel newFileChannel(Set<? extends OpenOption> options,
|
||||||
|
FileAttribute<?>... attrs)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
return zfs.newFileChannel(getResolvedPath(), options, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkAccess(AccessMode... modes) throws IOException {
|
||||||
|
boolean w = false;
|
||||||
|
boolean x = false;
|
||||||
|
for (AccessMode mode : modes) {
|
||||||
|
switch (mode) {
|
||||||
|
case READ:
|
||||||
|
break;
|
||||||
|
case WRITE:
|
||||||
|
w = true;
|
||||||
|
break;
|
||||||
|
case EXECUTE:
|
||||||
|
x = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZipFileAttributes attrs = zfs.getFileAttributes(getResolvedPath());
|
||||||
|
if (attrs == null && (path.length != 1 || path[0] != '/'))
|
||||||
|
throw new NoSuchFileException(toString());
|
||||||
|
if (w) {
|
||||||
|
if (zfs.isReadOnly())
|
||||||
|
throw new AccessDeniedException(toString());
|
||||||
|
}
|
||||||
|
if (x)
|
||||||
|
throw new AccessDeniedException(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean exists() {
|
||||||
|
if (path.length == 1 && path[0] == '/')
|
||||||
|
return true;
|
||||||
|
try {
|
||||||
|
return zfs.exists(getResolvedPath());
|
||||||
|
} catch (IOException x) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream newOutputStream(OpenOption... options) throws IOException
|
||||||
|
{
|
||||||
|
if (options.length == 0)
|
||||||
|
return zfs.newOutputStream(getResolvedPath(),
|
||||||
|
CREATE_NEW, WRITE);
|
||||||
|
return zfs.newOutputStream(getResolvedPath(), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move(ZipPath target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (Files.isSameFile(this.zfs.getZipFile(), target.zfs.getZipFile()))
|
||||||
|
{
|
||||||
|
zfs.copyFile(true,
|
||||||
|
getResolvedPath(), target.getResolvedPath(),
|
||||||
|
options);
|
||||||
|
} else {
|
||||||
|
copyToTarget(target, options);
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy(ZipPath target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (Files.isSameFile(this.zfs.getZipFile(), target.zfs.getZipFile()))
|
||||||
|
zfs.copyFile(false,
|
||||||
|
getResolvedPath(), target.getResolvedPath(),
|
||||||
|
options);
|
||||||
|
else
|
||||||
|
copyToTarget(target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyToTarget(ZipPath target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
boolean replaceExisting = false;
|
||||||
|
boolean copyAttrs = false;
|
||||||
|
for (CopyOption opt : options) {
|
||||||
|
if (opt == REPLACE_EXISTING)
|
||||||
|
replaceExisting = true;
|
||||||
|
else if (opt == COPY_ATTRIBUTES)
|
||||||
|
copyAttrs = true;
|
||||||
|
}
|
||||||
|
// attributes of source file
|
||||||
|
ZipFileAttributes zfas = getAttributes();
|
||||||
|
// check if target exists
|
||||||
|
boolean exists;
|
||||||
|
if (replaceExisting) {
|
||||||
|
try {
|
||||||
|
target.deleteIfExists();
|
||||||
|
exists = false;
|
||||||
|
} catch (DirectoryNotEmptyException x) {
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exists = target.exists();
|
||||||
|
}
|
||||||
|
if (exists)
|
||||||
|
throw new FileAlreadyExistsException(target.toString());
|
||||||
|
|
||||||
|
if (zfas.isDirectory()) {
|
||||||
|
// create directory or file
|
||||||
|
target.createDirectory();
|
||||||
|
} else {
|
||||||
|
InputStream is = zfs.newInputStream(getResolvedPath());
|
||||||
|
try {
|
||||||
|
OutputStream os = target.newOutputStream();
|
||||||
|
try {
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
int n = 0;
|
||||||
|
while ((n = is.read(buf)) != -1) {
|
||||||
|
os.write(buf, 0, n);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (copyAttrs) {
|
||||||
|
BasicFileAttributeView view =
|
||||||
|
ZipFileAttributeView.get(target, BasicFileAttributeView.class);
|
||||||
|
try {
|
||||||
|
view.setTimes(zfas.lastModifiedTime(),
|
||||||
|
zfas.lastAccessTime(),
|
||||||
|
zfas.creationTime());
|
||||||
|
} catch (IOException x) {
|
||||||
|
// rollback?
|
||||||
|
try {
|
||||||
|
target.delete();
|
||||||
|
} catch (IOException ignore) { }
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,320 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This source code is provided to illustrate the usage of a given feature
|
||||||
|
* or technique and has been deliberately simplified. Additional steps
|
||||||
|
* required for a production-quality application, such as security checks,
|
||||||
|
* input validation and proper error handling, might not be present in
|
||||||
|
* this sample code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.github.marschall.com.sun.nio.zipfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Xueming Shen
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ZipUtils {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a 16-bit short to the output stream in little-endian byte order.
|
||||||
|
*/
|
||||||
|
public static void writeShort(OutputStream os, int v) throws IOException {
|
||||||
|
os.write(v & 0xff);
|
||||||
|
os.write((v >>> 8) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a 32-bit int to the output stream in little-endian byte order.
|
||||||
|
*/
|
||||||
|
public static void writeInt(OutputStream os, long v) throws IOException {
|
||||||
|
os.write((int)(v & 0xff));
|
||||||
|
os.write((int)((v >>> 8) & 0xff));
|
||||||
|
os.write((int)((v >>> 16) & 0xff));
|
||||||
|
os.write((int)((v >>> 24) & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a 64-bit int to the output stream in little-endian byte order.
|
||||||
|
*/
|
||||||
|
public static void writeLong(OutputStream os, long v) throws IOException {
|
||||||
|
os.write((int)(v & 0xff));
|
||||||
|
os.write((int)((v >>> 8) & 0xff));
|
||||||
|
os.write((int)((v >>> 16) & 0xff));
|
||||||
|
os.write((int)((v >>> 24) & 0xff));
|
||||||
|
os.write((int)((v >>> 32) & 0xff));
|
||||||
|
os.write((int)((v >>> 40) & 0xff));
|
||||||
|
os.write((int)((v >>> 48) & 0xff));
|
||||||
|
os.write((int)((v >>> 56) & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes an array of bytes to the output stream.
|
||||||
|
*/
|
||||||
|
public static void writeBytes(OutputStream os, byte[] b)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
os.write(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes an array of bytes to the output stream.
|
||||||
|
*/
|
||||||
|
public static void writeBytes(OutputStream os, byte[] b, int off, int len)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
os.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a slash at the end, if it does not have one yet
|
||||||
|
*/
|
||||||
|
public static byte[] toDirectoryPath(byte[] dir) {
|
||||||
|
if (dir.length != 0 && dir[dir.length - 1] != '/') {
|
||||||
|
dir = Arrays.copyOf(dir, dir.length + 1);
|
||||||
|
dir[dir.length - 1] = '/';
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts DOS time to Java time (number of milliseconds since epoch).
|
||||||
|
*/
|
||||||
|
public static long dosToJavaTime(long dtime) {
|
||||||
|
Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
|
||||||
|
(int)(((dtime >> 21) & 0x0f) - 1),
|
||||||
|
(int)((dtime >> 16) & 0x1f),
|
||||||
|
(int)((dtime >> 11) & 0x1f),
|
||||||
|
(int)((dtime >> 5) & 0x3f),
|
||||||
|
(int)((dtime << 1) & 0x3e));
|
||||||
|
return d.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts Java time to DOS time.
|
||||||
|
*/
|
||||||
|
public static long javaToDosTime(long time) {
|
||||||
|
Date d = new Date(time);
|
||||||
|
int year = d.getYear() + 1900;
|
||||||
|
if (year < 1980) {
|
||||||
|
return (1 << 21) | (1 << 16);
|
||||||
|
}
|
||||||
|
return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
|
||||||
|
d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
|
||||||
|
d.getSeconds() >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// used to adjust values between Windows and java epoch
|
||||||
|
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
|
||||||
|
public static final long winToJavaTime(long wtime) {
|
||||||
|
return TimeUnit.MILLISECONDS.convert(
|
||||||
|
wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final long javaToWinTime(long time) {
|
||||||
|
return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS)
|
||||||
|
- WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final long unixToJavaTime(long utime) {
|
||||||
|
return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final long javaToUnixTime(long time) {
|
||||||
|
return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String regexMetaChars = ".^$+{[]|()";
|
||||||
|
private static final String globMetaChars = "\\*?[{";
|
||||||
|
private static boolean isRegexMeta(char c) {
|
||||||
|
return regexMetaChars.indexOf(c) != -1;
|
||||||
|
}
|
||||||
|
private static boolean isGlobMeta(char c) {
|
||||||
|
return globMetaChars.indexOf(c) != -1;
|
||||||
|
}
|
||||||
|
private static char EOL = 0; //TBD
|
||||||
|
private static char next(String glob, int i) {
|
||||||
|
if (i < glob.length()) {
|
||||||
|
return glob.charAt(i);
|
||||||
|
}
|
||||||
|
return EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a regex pattern from the given glob expression.
|
||||||
|
*
|
||||||
|
* @throws PatternSyntaxException
|
||||||
|
*/
|
||||||
|
public static String toRegexPattern(String globPattern) {
|
||||||
|
boolean inGroup = false;
|
||||||
|
StringBuilder regex = new StringBuilder("^");
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (i < globPattern.length()) {
|
||||||
|
char c = globPattern.charAt(i++);
|
||||||
|
switch (c) {
|
||||||
|
case '\\':
|
||||||
|
// escape special characters
|
||||||
|
if (i == globPattern.length()) {
|
||||||
|
throw new PatternSyntaxException("No character to escape",
|
||||||
|
globPattern, i - 1);
|
||||||
|
}
|
||||||
|
char next = globPattern.charAt(i++);
|
||||||
|
if (isGlobMeta(next) || isRegexMeta(next)) {
|
||||||
|
regex.append('\\');
|
||||||
|
}
|
||||||
|
regex.append(next);
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
regex.append(c);
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
// don't match name separator in class
|
||||||
|
regex.append("[[^/]&&[");
|
||||||
|
if (next(globPattern, i) == '^') {
|
||||||
|
// escape the regex negation char if it appears
|
||||||
|
regex.append("\\^");
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// negation
|
||||||
|
if (next(globPattern, i) == '!') {
|
||||||
|
regex.append('^');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// hyphen allowed at start
|
||||||
|
if (next(globPattern, i) == '-') {
|
||||||
|
regex.append('-');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean hasRangeStart = false;
|
||||||
|
char last = 0;
|
||||||
|
while (i < globPattern.length()) {
|
||||||
|
c = globPattern.charAt(i++);
|
||||||
|
if (c == ']') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == '/') {
|
||||||
|
throw new PatternSyntaxException("Explicit 'name separator' in class",
|
||||||
|
globPattern, i - 1);
|
||||||
|
}
|
||||||
|
// TBD: how to specify ']' in a class?
|
||||||
|
if (c == '\\' || c == '[' ||
|
||||||
|
c == '&' && next(globPattern, i) == '&') {
|
||||||
|
// escape '\', '[' or "&&" for regex class
|
||||||
|
regex.append('\\');
|
||||||
|
}
|
||||||
|
regex.append(c);
|
||||||
|
|
||||||
|
if (c == '-') {
|
||||||
|
if (!hasRangeStart) {
|
||||||
|
throw new PatternSyntaxException("Invalid range",
|
||||||
|
globPattern, i - 1);
|
||||||
|
}
|
||||||
|
if ((c = next(globPattern, i++)) == EOL || c == ']') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c < last) {
|
||||||
|
throw new PatternSyntaxException("Invalid range",
|
||||||
|
globPattern, i - 3);
|
||||||
|
}
|
||||||
|
regex.append(c);
|
||||||
|
hasRangeStart = false;
|
||||||
|
} else {
|
||||||
|
hasRangeStart = true;
|
||||||
|
last = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c != ']') {
|
||||||
|
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
|
||||||
|
}
|
||||||
|
regex.append("]]");
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
if (inGroup) {
|
||||||
|
throw new PatternSyntaxException("Cannot nest groups",
|
||||||
|
globPattern, i - 1);
|
||||||
|
}
|
||||||
|
regex.append("(?:(?:");
|
||||||
|
inGroup = true;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
if (inGroup) {
|
||||||
|
regex.append("))");
|
||||||
|
inGroup = false;
|
||||||
|
} else {
|
||||||
|
regex.append('}');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
if (inGroup) {
|
||||||
|
regex.append(")|(?:");
|
||||||
|
} else {
|
||||||
|
regex.append(',');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
if (next(globPattern, i) == '*') {
|
||||||
|
// crosses directory boundaries
|
||||||
|
regex.append(".*");
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// within directory boundary
|
||||||
|
regex.append("[^/]*");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
regex.append("[^/]");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isRegexMeta(c)) {
|
||||||
|
regex.append('\\');
|
||||||
|
}
|
||||||
|
regex.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inGroup) {
|
||||||
|
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
|
||||||
|
}
|
||||||
|
return regex.append('$').toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of Oracle nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,2 @@
|
||||||
|
com.github.marschall.com.sun.nio.zipfs.ZipFileSystemProvider
|
||||||
|
|
|
@ -21,3 +21,4 @@ include ':FCLCore'
|
||||||
include ':FCLauncher'
|
include ':FCLauncher'
|
||||||
include ':FCLLibrary'
|
include ':FCLLibrary'
|
||||||
include ':LWJGL'
|
include ':LWJGL'
|
||||||
|
include ':ZipFileSystem'
|
||||||
|
|
Loading…
Reference in New Issue