OpenJDK / amber / amber
changeset 6892:d954b585d3a1
Merge
author | lana |
---|---|
date | Wed, 13 Oct 2010 17:51:41 -0700 |
parents | 43cb79a6b123 750c1ccb2f2d |
children | cc3460420e3d |
files | langtools/test/tools/javac/processing/Xprint.java |
diffstat | 238 files changed, 11046 insertions(+), 1686 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ e02b4d709e177d08d56130a4bc68061e4bbacc7d jdk7-b109 a6442d6bc38a44152e0662688213ce4d2701f42a jdk7-b110 69f3edf083477955b5bd2f754252c7504167d8e1 jdk7-b111 +f960f117f1623629f64203e2b09a92a8f6f14ff5 jdk7-b112 +1fee41c7ed2b3388970a756a85aa693c0de8407a jdk7-b113
--- a/.hgtags-top-repo Tue Oct 12 17:05:28 2010 -0700 +++ b/.hgtags-top-repo Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ 81dfc728d7bb7e1fff4a4dc6d0f7cea5a3315667 jdk7-b109 2a02d4a6955c7c078aee9a604cb3be409800d82c jdk7-b110 9702d6fef68e17533ee7fcf5923b11ead3e912ce jdk7-b111 +b852103caf73da70068473777ae867a457bb3ae1 jdk7-b112 +c1df968c4527bfab5f97662a89245f15d12d378b jdk7-b113
--- a/Makefile Tue Oct 12 17:05:28 2010 -0700 +++ b/Makefile Wed Oct 13 17:51:41 2010 -0700 @@ -558,9 +558,12 @@ # rule to test ################################################################ -.NOTPARALLEL: test +.NOTPARALLEL: test_run -test: test_clean test_start test_summary +test: + $(MAKE) test_run + +test_run: test_clean test_start test_summary test_start: @$(ECHO) "Tests started at `$(DATE)`" @@ -586,7 +589,7 @@ # Get failure list from log $(OUTPUTDIR)/test_failures.txt: $(OUTPUTDIR)/test_log.txt @$(RM) $@ - @( $(EGREP) '^FAILED:' $< || $(ECHO) "" ) > $@ + @( $(EGREP) '^FAILED:' $< || $(ECHO) "" ) | $(NAWK) 'length>0' > $@ # Get log file of all tests run JDK_TO_TEST := $(shell \ @@ -598,10 +601,11 @@ $(ECHO) "$(PRODUCT_HOME)"; \ fi \ ) +TEST_TARGETS=all $(OUTPUTDIR)/test_log.txt: $(RM) $@ - ( $(CD) test && \ - $(MAKE) NO_STOPPING=- PRODUCT_HOME=$(JDK_TO_TEST) \ + ( $(CD) test && \ + $(MAKE) NO_STOPPING=- PRODUCT_HOME=$(JDK_TO_TEST) $(TEST_TARGETS) \ ) | tee $@ ################################################################ @@ -614,7 +618,7 @@ # PHONY ################################################################ -.PHONY: all test test_start test_summary test_clean \ +.PHONY: all test test_run test_start test_summary test_clean \ generic_build_repo_series \ what clobber insane \ dev dev-build dev-sanity dev-clobber \
--- a/corba/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/corba/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ c3dd858e09b20206459d9e7b0ead99d27ab00eab jdk7-b109 0e1f80fda2271f53d4bbb59ec3f301dfbcef6a0a jdk7-b110 640fa4d4e2ad4c2d7e4815c955026740d8c52b7a jdk7-b111 +cc67fdc4fee9a5b25caee4e71b51a8ff24ae7d1a jdk7-b112 +a89a6c5be9d1a754868d3d359cbf7ad36aa95631 jdk7-b113
--- a/hotspot/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/hotspot/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -121,3 +121,5 @@ cc4bb3022b3144dc5db0805b9ef6c7eff2aa3b81 jdk7-b109 2f25f2b8de2700a1822463b1bd3d02b5e218018f jdk7-b110 07b042e13dde4f3479ba9ec55120fcd5e8623323 jdk7-b111 +5511edd5d719f3fc9fdd04879482026a3d2c8652 jdk7-b112 +beef35b96b81129c375d572357fb9548d9020db1 jdk7-b113
--- a/jaxp/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/jaxp/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ 0f382d6120fc07aed2209484a42458cabf405916 jdk7-b109 d422dbdd09766269344b796b3a46a5b3f74557e1 jdk7-b110 8106c747067c905d814a737a57fea0e29057b33f jdk7-b111 +1b05254242881527b4d5d711295c0fe708c8823a jdk7-b112 +bc0c84ce54c34d3e8b0604b94da0d7c75c26755e jdk7-b113
--- a/jaxws/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/jaxws/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ 4f626e0d70bda68c76bbd0e89d2bc2407f979736 jdk7-b109 95ecac35fb11530752bd0404c9bf02bcfb30990e jdk7-b110 2575ebca96c7fb1b78f6ae025a97321210aba309 jdk7-b111 +8e0f0054817f0f73fb33e80fb1333fb45b1d513d jdk7-b112 +d35c94fd22362f478f75b4bfcd2bef6a83cb9b3f jdk7-b113
--- a/jdk/.hgtags Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/.hgtags Wed Oct 13 17:51:41 2010 -0700 @@ -86,3 +86,5 @@ ab0d3f54a63f2aadfcdd2e14b81f79362ce454e2 jdk7-b109 176586cd040e4dd17a5ff6e91f72df10d7442453 jdk7-b110 fb63a2688db807a73e2a3de7d9bab298f1bff0e8 jdk7-b111 +b53f226b1d91473ac54184afa827be07b87e0319 jdk7-b112 +61d3b9fbb26bdef56cfa41b9af5bc312a22cbeb8 jdk7-b113
--- a/jdk/make/mkdemo/Makefile Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/make/mkdemo/Makefile Wed Oct 13 17:51:41 2010 -0700 @@ -31,7 +31,7 @@ PRODUCT = demos include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = jni +SUBDIRS = jni nio SUBDIRS_desktop = applets jfc SUBDIRS_management = management SUBDIRS_misc = scripting
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/mkdemo/nio/Makefile Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,39 @@ +# +# Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building the jfc demos +# + +BUILDDIR = ../.. +PRODUCT = demos +include $(BUILDDIR)/common/Defs.gmk + +SUBDIRS = zipfs +include $(BUILDDIR)/common/Subdirs.gmk + +all build clean clobber:: + $(SUBDIRS-loop) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/mkdemo/nio/zipfs/Makefile Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,44 @@ +# +# Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile to build the ZipFileSystem demo. +# + +BUILDDIR = ../../.. +PRODUCT = demo/zipfs +DEMONAME = zipfs +include $(BUILDDIR)/common/Defs.gmk + +DEMO_ROOT = $(SHARE_SRC)/demo/nio/$(DEMONAME) +DEMO_TOPFILES = ./README.txt +DEMO_SRCDIR = $(DEMO_ROOT) +DEMO_DESTDIR = $(DEMODIR)/nio/$(DEMONAME) + +# +# Demo jar building rules. +# +include $(BUILDDIR)/common/Demo.gmk +
--- a/jdk/make/sun/cmm/lcms/Makefile Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/make/sun/cmm/lcms/Makefile Wed Oct 13 17:51:41 2010 -0700 @@ -80,7 +80,12 @@ vpath %.c $(SHARE_SRC)/native/sun/java2d ifeq ($(PLATFORM), windows) -OTHER_CFLAGS += -DCMS_IS_WINDOWS_ -Dsqrtf=sqrt +OTHER_CFLAGS += -DCMS_IS_WINDOWS_ + +ifeq ($(COMPILER_VERSION), VS2003) +OTHER_CFLAGS += -Dsqrtf=sqrt +endif + OTHER_LDLIBS = $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib OTHER_INCLUDES += -I$(SHARE_SRC)/native/sun/java2d \ -I$(SHARE_SRC)/native/sun/awt/debug
--- a/jdk/src/share/classes/java/awt/Dialog.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/awt/Dialog.java Wed Oct 13 17:51:41 2010 -0700 @@ -1068,7 +1068,7 @@ modalityPushed(); try { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); - secondaryLoop = eventQueue.createSecondaryLoop(cond, modalFilter, 5000); + secondaryLoop = eventQueue.createSecondaryLoop(cond, modalFilter, 0); if (!secondaryLoop.enter()) { secondaryLoop = null; }
--- a/jdk/src/share/classes/java/awt/KeyboardFocusManager.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/awt/KeyboardFocusManager.java Wed Oct 13 17:51:41 2010 -0700 @@ -142,6 +142,9 @@ public void removeLastFocusRequest(Component heavyweight) { KeyboardFocusManager.removeLastFocusRequest(heavyweight); } + public void setMostRecentFocusOwner(Window window, Component component) { + KeyboardFocusManager.setMostRecentFocusOwner(window, component); + } } ); }
--- a/jdk/src/share/classes/java/awt/event/ActionEvent.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/awt/event/ActionEvent.java Wed Oct 13 17:51:41 2010 -0700 @@ -51,7 +51,7 @@ * in the range from {@code ACTION_FIRST} to {@code ACTION_LAST}. * * @see ActionListener - * @see <a href="http://java.sun.com/docs/books/tutorial/post1.0/ui/eventmodel.html">Tutorial: Java 1.1 Event Model</a> + * @see <a href="http://java.sun.com/docs/books/tutorial/uiswing/events/actionlistener.html">Tutorial: How to Write an Action Listener</a> * * @author Carl Quinn * @since 1.1
--- a/jdk/src/share/classes/java/awt/image/SampleModel.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/awt/image/SampleModel.java Wed Oct 13 17:51:41 2010 -0700 @@ -937,14 +937,22 @@ int iArray[], DataBuffer data) { int pixels[]; int Offset=0; + int x1 = x + w; + int y1 = y + h; + + if (x < 0 || x1 < x || x1 > width || + y < 0 || y1 < y || y1 > height) + { + throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); + } if (iArray != null) pixels = iArray; else pixels = new int[w * h]; - for(int i=y; i<(h+y); i++) { - for (int j=x; j<(w+x); j++) { + for(int i=y; i<y1; i++) { + for (int j=x; j<x1; j++) { pixels[Offset++] = getSample(j, i, b, data); } } @@ -978,14 +986,22 @@ DataBuffer data) { float pixels[]; int Offset=0; + int x1 = x + w; + int y1 = y + h; + + if (x < 0 || x1 < x || x1 > width || + y < 0 || y1 < y || y1 > height) + { + throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); + } if (fArray != null) pixels = fArray; else pixels = new float[w * h]; - for (int i=y; i<(h+y); i++) { - for (int j=x; j<(w+x); j++) { + for (int i=y; i<y1; i++) { + for (int j=x; j<x1; j++) { pixels[Offset++] = getSampleFloat(j, i, b, data); } } @@ -1019,14 +1035,22 @@ DataBuffer data) { double pixels[]; int Offset=0; + int x1 = x + w; + int y1 = y + h; + + if (x < 0 || x1 < x || x1 > width || + y < 0 || y1 < y || y1 > height) + { + throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); + } if (dArray != null) pixels = dArray; else pixels = new double[w * h]; - for (int i=y; i<(y+h); i++) { - for (int j=x; j<(x+w); j++) { + for (int i=y; i<y1; i++) { + for (int j=x; j<x1; j++) { pixels[Offset++] = getSampleDouble(j, i, b, data); } }
--- a/jdk/src/share/classes/java/beans/EventSetDescriptor.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/beans/EventSetDescriptor.java Wed Oct 13 17:51:41 2010 -0700 @@ -176,8 +176,9 @@ setRemoveListenerMethod(getMethod(sourceClass, removeListenerMethodName, 1)); // Be more forgiving of not finding the getListener method. - if (getListenerMethodName != null) { - setGetListenerMethod(Introspector.findInstanceMethod(sourceClass, getListenerMethodName)); + Method method = Introspector.findMethod(sourceClass, getListenerMethodName, 0); + if (method != null) { + setGetListenerMethod(method); } }
--- a/jdk/src/share/classes/java/beans/IndexedPropertyDescriptor.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/beans/IndexedPropertyDescriptor.java Wed Oct 13 17:51:41 2010 -0700 @@ -189,11 +189,13 @@ indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); } } - indexedReadMethod = Introspector.findInstanceMethod(cls, indexedReadMethodName, int.class); + + Class[] args = { int.class }; + indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); if (indexedReadMethod == null) { // no "is" method, so look for a "get" method. indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); - indexedReadMethod = Introspector.findInstanceMethod(cls, indexedReadMethodName, int.class); + indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); } setIndexedReadMethod0(indexedReadMethod); } @@ -265,7 +267,9 @@ if (indexedWriteMethodName == null) { indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName(); } - indexedWriteMethod = Introspector.findInstanceMethod(cls, indexedWriteMethodName, int.class, type); + + Class[] args = (type == null) ? null : new Class[] { int.class, type }; + indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, 2, args); if (indexedWriteMethod != null) { if (!indexedWriteMethod.getReturnType().equals(void.class)) { indexedWriteMethod = null;
--- a/jdk/src/share/classes/java/beans/Introspector.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/beans/Introspector.java Wed Oct 13 17:51:41 2010 -0700 @@ -28,7 +28,6 @@ import com.sun.beans.WeakCache; import com.sun.beans.finder.BeanInfoFinder; import com.sun.beans.finder.ClassFinder; -import com.sun.beans.finder.MethodFinder; import java.lang.ref.Reference; import java.lang.ref.SoftReference; @@ -843,8 +842,8 @@ Method read = result.getReadMethod(); if (read == null && write != null) { - read = findInstanceMethod(result.getClass0(), - GET_PREFIX + NameGenerator.capitalize(result.getName())); + read = findMethod(result.getClass0(), + GET_PREFIX + NameGenerator.capitalize(result.getName()), 0); if (read != null) { try { result.setReadMethod(read); @@ -854,9 +853,9 @@ } } if (write == null && read != null) { - write = findInstanceMethod(result.getClass0(), - SET_PREFIX + NameGenerator.capitalize(result.getName()), - FeatureDescriptor.getReturnType(result.getClass0(), read)); + write = findMethod(result.getClass0(), + SET_PREFIX + NameGenerator.capitalize(result.getName()), 1, + new Class[] { FeatureDescriptor.getReturnType(result.getClass0(), read) }); if (write != null) { try { result.setWriteMethod(write); @@ -1280,27 +1279,90 @@ // Package private support methods. //====================================================================== - static Method findMethod(Class<?> type, String name, int args) { - for (Method method : type.getMethods()) { - if (method.getName().equals(name) && (args == method.getParameterTypes().length)) { - try { - return MethodFinder.findAccessibleMethod(method); + /** + * Internal support for finding a target methodName with a given + * parameter list on a given class. + */ + private static Method internalFindMethod(Class start, String methodName, + int argCount, Class args[]) { + // For overriden methods we need to find the most derived version. + // So we start with the given class and walk up the superclass chain. + + Method method = null; + + for (Class cl = start; cl != null; cl = cl.getSuperclass()) { + Method methods[] = getPublicDeclaredMethods(cl); + for (int i = 0; i < methods.length; i++) { + method = methods[i]; + if (method == null) { + continue; } - catch (NoSuchMethodException exception) { - // continue search for a method with the specified count of parameters + + // make sure method signature matches. + Class params[] = FeatureDescriptor.getParameterTypes(start, method); + if (method.getName().equals(methodName) && + params.length == argCount) { + if (args != null) { + boolean different = false; + if (argCount > 0) { + for (int j = 0; j < argCount; j++) { + if (params[j] != args[j]) { + different = true; + continue; + } + } + if (different) { + continue; + } + } + } + return method; } } } - return null; + method = null; + + // Now check any inherited interfaces. This is necessary both when + // the argument class is itself an interface, and when the argument + // class is an abstract class. + Class ifcs[] = start.getInterfaces(); + for (int i = 0 ; i < ifcs.length; i++) { + // Note: The original implementation had both methods calling + // the 3 arg method. This is preserved but perhaps it should + // pass the args array instead of null. + method = internalFindMethod(ifcs[i], methodName, argCount, null); + if (method != null) { + break; + } + } + return method; } - static Method findInstanceMethod(Class<?> type, String name, Class<?>... args) { - try { - return MethodFinder.findInstanceMethod(type, name, args); - } - catch (NoSuchMethodException exception) { + /** + * Find a target methodName on a given class. + */ + static Method findMethod(Class cls, String methodName, int argCount) { + return findMethod(cls, methodName, argCount, null); + } + + /** + * Find a target methodName with specific parameter list on a given class. + * <p> + * Used in the contructors of the EventSetDescriptor, + * PropertyDescriptor and the IndexedPropertyDescriptor. + * <p> + * @param cls The Class object on which to retrieve the method. + * @param methodName Name of the method. + * @param argCount Number of arguments for the desired method. + * @param args Array of argument types for the method. + * @return the method or null if not found + */ + static Method findMethod(Class cls, String methodName, int argCount, + Class args[]) { + if (methodName == null) { return null; } + return internalFindMethod(cls, methodName, argCount, args); } /**
--- a/jdk/src/share/classes/java/beans/MethodDescriptor.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/beans/MethodDescriptor.java Wed Oct 13 17:51:41 2010 -0700 @@ -90,13 +90,13 @@ // Find methods for up to 2 params. We are guessing here. // This block should never execute unless the classloader // that loaded the argument classes disappears. - method = Introspector.findMethod(cls, name, i); + method = Introspector.findMethod(cls, name, i, null); if (method != null) { break; } } } else { - method = Statement.getMethod(cls, name, params); + method = Introspector.findMethod(cls, name, params.length, params); } setMethod(method); }
--- a/jdk/src/share/classes/java/beans/PropertyDescriptor.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/beans/PropertyDescriptor.java Wed Oct 13 17:51:41 2010 -0700 @@ -112,7 +112,8 @@ // If this class or one of its base classes allow PropertyChangeListener, // then we assume that any properties we discover are "bound". // See Introspector.getTargetPropertyInfo() method. - this.bound = null != Introspector.findInstanceMethod(beanClass, "addPropertyChangeListener", PropertyChangeListener.class); + Class[] args = { PropertyChangeListener.class }; + this.bound = null != Introspector.findMethod(beanClass, "addPropertyChangeListener", args.length, args); } /** @@ -223,10 +224,10 @@ // property type is. For booleans, there can be "is" and "get" // methods. If an "is" method exists, this is the official // reader method so look for this one first. - readMethod = Introspector.findInstanceMethod(cls, readMethodName); + readMethod = Introspector.findMethod(cls, readMethodName, 0); if (readMethod == null) { readMethodName = Introspector.GET_PREFIX + getBaseName(); - readMethod = Introspector.findInstanceMethod(cls, readMethodName); + readMethod = Introspector.findMethod(cls, readMethodName, 0); } try { setReadMethod(readMethod); @@ -291,7 +292,8 @@ writeMethodName = Introspector.SET_PREFIX + getBaseName(); } - writeMethod = Introspector.findInstanceMethod(cls, writeMethodName, type); + Class[] args = (type == null) ? null : new Class[] { type }; + writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args); if (writeMethod != null) { if (!writeMethod.getReturnType().equals(void.class)) { writeMethod = null;
--- a/jdk/src/share/classes/java/util/Locale.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/util/Locale.java Wed Oct 13 17:51:41 2010 -0700 @@ -569,6 +569,9 @@ * @exception NullPointerException thrown if any argument is null. */ public Locale(String language, String country, String variant) { + if (language== null || country == null || variant == null) { + throw new NullPointerException(); + } _baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), "", country, variant); _extensions = getCompatibilityExtensions(language, "", country, variant); }
--- a/jdk/src/share/classes/java/util/ResourceBundle.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/java/util/ResourceBundle.java Wed Oct 13 17:51:41 2010 -0700 @@ -293,16 +293,6 @@ = new ConcurrentHashMap<CacheKey, BundleReference>(INITIAL_CACHE_SIZE); /** - * This ConcurrentMap is used to keep multiple threads from loading the - * same bundle concurrently. The table entries are <CacheKey, Thread> - * where CacheKey is the key for the bundle that is under construction - * and Thread is the thread that is constructing the bundle. - * This list is manipulated in findBundleInCache and putBundleInCache. - */ - private static final ConcurrentMap<CacheKey, Thread> underConstruction - = new ConcurrentHashMap<CacheKey, Thread>(); - - /** * Queue for reference objects referring to class loaders or bundles. */ private static final ReferenceQueue referenceQueue = new ReferenceQueue(); @@ -1381,7 +1371,7 @@ boolean expiredBundle = false; // First, look up the cache to see if it's in the cache, without - // declaring beginLoading. + // attempting to load bundle. cacheKey.setLocale(targetLocale); ResourceBundle bundle = findBundleInCache(cacheKey, control); if (isValidBundle(bundle)) { @@ -1408,56 +1398,25 @@ CacheKey constKey = (CacheKey) cacheKey.clone(); try { - // Try declaring loading. If beginLoading() returns true, - // then we can proceed. Otherwise, we need to take a look - // at the cache again to see if someone else has loaded - // the bundle and put it in the cache while we've been - // waiting for other loading work to complete. - while (!beginLoading(constKey)) { - bundle = findBundleInCache(cacheKey, control); - if (bundle == null) { - continue; + bundle = loadBundle(cacheKey, formats, control, expiredBundle); + if (bundle != null) { + if (bundle.parent == null) { + bundle.setParent(parent); } - if (bundle == NONEXISTENT_BUNDLE) { - // If the bundle is NONEXISTENT_BUNDLE, the bundle doesn't exist. - return parent; - } - expiredBundle = bundle.expired; - if (!expiredBundle) { - if (bundle.parent == parent) { - return bundle; - } - BundleReference bundleRef = cacheList.get(cacheKey); - if (bundleRef != null && bundleRef.get() == bundle) { - cacheList.remove(cacheKey, bundleRef); - } - } + bundle.locale = targetLocale; + bundle = putBundleInCache(cacheKey, bundle, control); + return bundle; } - try { - bundle = loadBundle(cacheKey, formats, control, expiredBundle); - if (bundle != null) { - if (bundle.parent == null) { - bundle.setParent(parent); - } - bundle.locale = targetLocale; - bundle = putBundleInCache(cacheKey, bundle, control); - return bundle; - } - - // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle - // instance for the locale. - putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control); - } finally { - endLoading(constKey); - } + // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle + // instance for the locale. + putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control); } finally { if (constKey.getCause() instanceof InterruptedException) { Thread.currentThread().interrupt(); } } } - assert underConstruction.get(cacheKey) != Thread.currentThread(); return parent; } @@ -1465,7 +1424,6 @@ List<String> formats, Control control, boolean reload) { - assert underConstruction.get(cacheKey) == Thread.currentThread(); // Here we actually load the bundle in the order of formats // specified by the getFormats() value. @@ -1498,7 +1456,6 @@ break; } } - assert underConstruction.get(cacheKey) == Thread.currentThread(); return bundle; } @@ -1530,57 +1487,6 @@ } /** - * Declares the beginning of actual resource bundle loading. This method - * returns true if the declaration is successful and the current thread has - * been put in underConstruction. If someone else has already begun - * loading, this method waits until that loading work is complete and - * returns false. - */ - private static final boolean beginLoading(CacheKey constKey) { - Thread me = Thread.currentThread(); - Thread worker; - // We need to declare by putting the current Thread (me) to - // underConstruction that we are working on loading the specified - // resource bundle. If we are already working the loading, it means - // that the resource loading requires a recursive call. In that case, - // we have to proceed. (4300693) - if (((worker = underConstruction.putIfAbsent(constKey, me)) == null) - || worker == me) { - return true; - } - - // If someone else is working on the loading, wait until - // the Thread finishes the bundle loading. - synchronized (worker) { - while (underConstruction.get(constKey) == worker) { - try { - worker.wait(); - } catch (InterruptedException e) { - // record the interruption - constKey.setCause(e); - } - } - } - return false; - } - - /** - * Declares the end of the bundle loading. This method calls notifyAll - * for those who are waiting for this completion. - */ - private static final void endLoading(CacheKey constKey) { - // Remove this Thread from the underConstruction map and wake up - // those who have been waiting for me to complete this bundle - // loading. - Thread me = Thread.currentThread(); - assert (underConstruction.get(constKey) == me); - underConstruction.remove(constKey); - synchronized (me) { - me.notifyAll(); - } - } - - /** * Throw a MissingResourceException with proper message */ private static final void throwMissingResourceException(String baseName,
--- a/jdk/src/share/classes/javax/swing/GroupLayout.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/GroupLayout.java Wed Oct 13 17:51:41 2010 -0700 @@ -1464,8 +1464,8 @@ * <= {@code pref} <= {@code max}. * <p> * Similarly any methods that take a {@code Component} throw a - * {@code NullPointerException} if passed {@code null} and any methods - * that take a {@code Group} throw an {@code IllegalArgumentException} if + * {@code IllegalArgumentException} if passed {@code null} and any methods + * that take a {@code Group} throw an {@code NullPointerException} if * passed {@code null}. * * @see #createSequentialGroup
--- a/jdk/src/share/classes/javax/swing/JComponent.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/JComponent.java Wed Oct 13 17:51:41 2010 -0700 @@ -4787,6 +4787,17 @@ * @see RepaintManager#addDirtyRegion */ public void repaint(long tm, int x, int y, int width, int height) { + Container p = this; + while ((p = p.getParent()) instanceof JComponent) { + JComponent jp = (JComponent) p; + if (jp.isPaintingOrigin()) { + Rectangle rectangle = SwingUtilities.convertRectangle( + this, new Rectangle(x, y, width, height), jp); + jp.repaint(tm, + rectangle.x, rectangle.y, rectangle.width, rectangle.height); + return; + } + } RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height); }
--- a/jdk/src/share/classes/javax/swing/JDesktopPane.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/JDesktopPane.java Wed Oct 13 17:51:41 2010 -0700 @@ -215,7 +215,8 @@ /** * Sets the <code>DesktopManger</code> that will handle - * desktop-specific UI actions. + * desktop-specific UI actions. This may be overridden by + * {@code LookAndFeel}. * * @param d the <code>DesktopManager</code> to use *
--- a/jdk/src/share/classes/javax/swing/JLayer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/JLayer.java Wed Oct 13 17:51:41 2010 -0700 @@ -25,17 +25,17 @@ package javax.swing; +import sun.awt.AWTAccessor; + import javax.swing.plaf.LayerUI; +import javax.swing.border.Border; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.Serializable; -import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.security.AccessController; import java.security.PrivilegedAction; @@ -156,8 +156,6 @@ private LayerUI<? super V> layerUI; private JPanel glassPane; private boolean isPainting; - private static final DefaultLayerLayout sharedLayoutInstance = - new DefaultLayerLayout(); private long eventMask; private static final LayerEventController eventController = @@ -165,7 +163,7 @@ /** * Creates a new {@code JLayer} object with a {@code null} view component - * and {@code null} {@link javax.swing.plaf.LayerUI}. + * and default {@link javax.swing.plaf.LayerUI}. * * @see #setView * @see #setUI @@ -176,14 +174,14 @@ /** * Creates a new {@code JLayer} object - * with {@code null} {@link javax.swing.plaf.LayerUI}. + * with default {@link javax.swing.plaf.LayerUI}. * * @param view the component to be decorated by this {@code JLayer} * * @see #setUI */ public JLayer(V view) { - this(view, null); + this(view, new LayerUI<V>()); } /** @@ -195,7 +193,6 @@ * to be used by this {@code JLayer} */ public JLayer(V view, LayerUI<V> ui) { - setLayout(sharedLayoutInstance); setGlassPane(createGlassPane()); setView(view); setUI(ui); @@ -279,10 +276,15 @@ */ public void setGlassPane(JPanel glassPane) { Component oldGlassPane = getGlassPane(); + boolean isGlassPaneVisible = false; if (oldGlassPane != null) { + isGlassPaneVisible = oldGlassPane.isVisible(); super.remove(oldGlassPane); } if (glassPane != null) { + AWTAccessor.getComponentAccessor().setMixingCutoutShape(glassPane, + new Rectangle()); + glassPane.setVisible(isGlassPaneVisible); super.addImpl(glassPane, null, 0); } this.glassPane = glassPane; @@ -303,6 +305,40 @@ } /** + * Sets the layout manager for this container. This method is + * overridden to prevent the layout manager from being set. + * <p/>Note: If {@code mgr} is non-{@code null}, this + * method will throw an exception as layout managers are not supported on + * a {@code JLayer}. + * + * @param mgr the specified layout manager + * @exception IllegalArgumentException this method is not supported + */ + public void setLayout(LayoutManager mgr) { + if (mgr != null) { + throw new IllegalArgumentException("JLayer.setLayout() not supported"); + } + } + + /** + * A non-{@code null] border, or non-zero insets, isn't supported, to prevent the geometry + * of this component from becoming complex enough to inhibit + * subclassing of {@code LayerUI} class. To create a {@code JLayer} with a border, + * add it to a {@code JPanel} that has a border. + * <p/>Note: If {@code border} is non-{@code null}, this + * method will throw an exception as borders are not supported on + * a {@code JLayer}. + * + * @param border the {@code Border} to set + * @exception IllegalArgumentException this method is not supported + */ + public void setBorder(Border border) { + if (border != null) { + throw new IllegalArgumentException("JLayer.setBorder() not supported"); + } + } + + /** * This method is not supported by {@code JLayer} * and always throws {@code UnsupportedOperationException} * @@ -341,6 +377,32 @@ } /** + * Always returns {@code true} to cause painting to originate from {@code JLayer}, + * or one of its ancestors. + * + * @return true + * @see JComponent#isPaintingOrigin() + */ + boolean isPaintingOrigin() { + return true; + } + + /** + * Delegates repainting to {@link javax.swing.plaf.LayerUI#repaint} method. + * + * @param tm this parameter is not used + * @param x the x value of the dirty region + * @param y the y value of the dirty region + * @param width the width of the dirty region + * @param height the height of the dirty region + */ + public void repaint(long tm, int x, int y, int width, int height) { + if (getUI() != null) { + getUI().repaint(tm, x, y, width, height, this); + } + } + + /** * Delegates all painting to the {@link javax.swing.plaf.LayerUI} object. * * @param g the {@code Graphics} to render to @@ -364,14 +426,18 @@ } /** - * To enable the correct painting of the {@code glassPane} and view component, - * the {@code JLayer} overrides the default implementation of - * this method to return {@code false} when the {@code glassPane} is visible. + * The {@code JLayer} overrides the default implementation of + * this method (in {@code JComponent}) to return {@code false}. + * This ensures + * that the drawing machinery will call the {@code JLayer}'s + * {@code paint} + * implementation rather than messaging the {@code JLayer}'s + * children directly. * - * @return false if {@code JLayer}'s {@code glassPane} is visible + * @return false */ public boolean isOptimizedDrawingEnabled() { - return glassPane == null || !glassPane.isVisible(); + return false; } /** @@ -461,17 +527,16 @@ /** * Returns the preferred size of the viewport for a view component. * <p/> - * If the ui delegate of this layer is not {@code null}, this method delegates its - * implementation to the {@code LayerUI.getPreferredScrollableViewportSize(JLayer)} + * If the view component of this layer implements {@link Scrollable}, this method delegates its + * implementation to the view component. * * @return the preferred size of the viewport for a view component * * @see Scrollable - * @see LayerUI#getPreferredScrollableViewportSize(JLayer) */ public Dimension getPreferredScrollableViewportSize() { - if (getUI() != null) { - return getUI().getPreferredScrollableViewportSize(this); + if (getView() instanceof Scrollable) { + return ((Scrollable)getView()).getPreferredScrollableViewportSize(); } return getPreferredSize(); } @@ -481,18 +546,17 @@ * that display logical rows or columns in order to completely expose * one block of rows or columns, depending on the value of orientation. * <p/> - * If the ui delegate of this layer is not {@code null}, this method delegates its - * implementation to the {@code LayerUI.getScrollableBlockIncrement(JLayer,Rectangle,int,int)} + * If the view component of this layer implements {@link Scrollable}, this method delegates its + * implementation to the view component. * * @return the "block" increment for scrolling in the specified direction * * @see Scrollable - * @see LayerUI#getScrollableBlockIncrement(JLayer, Rectangle, int, int) */ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - if (getUI() != null) { - return getUI().getScrollableBlockIncrement(this, visibleRect, + if (getView() instanceof Scrollable) { + return ((Scrollable)getView()).getScrollableBlockIncrement(visibleRect, orientation, direction); } return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : @@ -504,17 +568,16 @@ * determine the height of the layer, unless the preferred height * of the layer is smaller than the height of the viewport. * <p/> - * If the ui delegate of this layer is not null, this method delegates its - * implementation to the {@code LayerUI.getScrollableTracksViewportHeight(JLayer)} + * If the view component of this layer implements {@link Scrollable}, this method delegates its + * implementation to the view component. * * @return whether the layer should track the height of the viewport * * @see Scrollable - * @see LayerUI#getScrollableTracksViewportHeight(JLayer) */ public boolean getScrollableTracksViewportHeight() { - if (getUI() != null) { - return getUI().getScrollableTracksViewportHeight(this); + if (getView() instanceof Scrollable) { + return ((Scrollable)getView()).getScrollableTracksViewportHeight(); } return false; } @@ -524,17 +587,16 @@ * determine the width of the layer, unless the preferred width * of the layer is smaller than the width of the viewport. * <p/> - * If the ui delegate of this layer is not null, this method delegates its - * implementation to the {@code LayerUI.getScrollableTracksViewportWidth(JLayer)} + * If the view component of this layer implements {@link Scrollable}, this method delegates its + * implementation to the view component. * * @return whether the layer should track the width of the viewport * * @see Scrollable - * @see LayerUI#getScrollableTracksViewportWidth(JLayer) */ public boolean getScrollableTracksViewportWidth() { - if (getUI() != null) { - return getUI().getScrollableTracksViewportWidth(this); + if (getView() instanceof Scrollable) { + return ((Scrollable)getView()).getScrollableTracksViewportWidth(); } return false; } @@ -549,20 +611,19 @@ * Scrolling containers, like {@code JScrollPane}, will use this method * each time the user requests a unit scroll. * <p/> - * If the ui delegate of this layer is not {@code null}, this method delegates its - * implementation to the {@code LayerUI.getScrollableUnitIncrement(JLayer,Rectangle,int,int)} + * If the view component of this layer implements {@link Scrollable}, this method delegates its + * implementation to the view component. * * @return The "unit" increment for scrolling in the specified direction. * This value should always be positive. * * @see Scrollable - * @see LayerUI#getScrollableUnitIncrement(JLayer, Rectangle, int, int) */ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - if (getUI() != null) { - return getUI().getScrollableUnitIncrement( - this, visibleRect, orientation, direction); + if (getView() instanceof Scrollable) { + return ((Scrollable) getView()).getScrollableUnitIncrement( + visibleRect, orientation, direction); } return 1; } @@ -595,6 +656,16 @@ } /** + * Delegates its functionality to the {@link javax.swing.plaf.LayerUI#doLayout(JLayer)} method, + * if {@code LayerUI} is set. + */ + public void doLayout() { + if (getUI() != null) { + getUI().doLayout(this); + } + } + + /** * static AWTEventListener to be shared with all AbstractLayerUIs */ private static class LayerEventController implements AWTEventListener { @@ -625,8 +696,8 @@ JLayer l = (JLayer) component; LayerUI ui = l.getUI(); if (ui != null && - isEventEnabled(l.getLayerEventMask(), - event.getID())) { + isEventEnabled(l.getLayerEventMask(), event.getID()) && + (!(event instanceof InputEvent) || !((InputEvent)event).isConsumed())) { ui.eventDispatched(event, l); } } @@ -758,82 +829,4 @@ return super.contains(x, y); } } - - /** - * The default layout manager for the {@link javax.swing.JLayer}.<br/> - * It places the glassPane on top of the view component - * and makes it the same size as {@code JLayer}, - * it also makes the view component the same size but minus layer's insets<br/> - */ - private static class DefaultLayerLayout implements LayoutManager, Serializable { - /** - * {@inheritDoc} - */ - public void layoutContainer(Container parent) { - JLayer layer = (JLayer) parent; - Component view = layer.getView(); - Component glassPane = layer.getGlassPane(); - if (view != null) { - Insets insets = layer.getInsets(); - view.setLocation(insets.left, insets.top); - view.setSize(layer.getWidth() - insets.left - insets.right, - layer.getHeight() - insets.top - insets.bottom); - } - if (glassPane != null) { - glassPane.setLocation(0, 0); - glassPane.setSize(layer.getWidth(), layer.getHeight()); - } - } - - /** - * {@inheritDoc} - */ - public Dimension minimumLayoutSize(Container parent) { - JLayer layer = (JLayer) parent; - Insets insets = layer.getInsets(); - Dimension ret = new Dimension(insets.left + insets.right, - insets.top + insets.bottom); - Component view = layer.getView(); - if (view != null) { - Dimension size = view.getMinimumSize(); - ret.width += size.width; - ret.height += size.height; - } - if (ret.width == 0 || ret.height == 0) { - ret.width = ret.height = 4; - } - return ret; - } - - /** - * {@inheritDoc} - */ - public Dimension preferredLayoutSize(Container parent) { - JLayer layer = (JLayer) parent; - Insets insets = layer.getInsets(); - Dimension ret = new Dimension(insets.left + insets.right, - insets.top + insets.bottom); - Component view = layer.getView(); - if (view != null) { - Dimension size = view.getPreferredSize(); - if (size.width > 0 && size.height > 0) { - ret.width += size.width; - ret.height += size.height; - } - } - return ret; - } - - /** - * {@inheritDoc} - */ - public void addLayoutComponent(String name, Component comp) { - } - - /** - * {@inheritDoc} - */ - public void removeLayoutComponent(Component comp) { - } - } }
--- a/jdk/src/share/classes/javax/swing/JTable.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/JTable.java Wed Oct 13 17:51:41 2010 -0700 @@ -4574,9 +4574,8 @@ * @see TableColumnModelListener */ public void columnMoved(TableColumnModelEvent e) { - // If I'm currently editing, then I should stop editing - if (isEditing()) { - removeEditor(); + if (isEditing() && !getCellEditor().stopCellEditing()) { + getCellEditor().cancelCellEditing(); } repaint(); } @@ -4593,8 +4592,8 @@ * @see TableColumnModelListener */ public void columnMarginChanged(ChangeEvent e) { - if (isEditing()) { - removeEditor(); + if (isEditing() && !getCellEditor().stopCellEditing()) { + getCellEditor().cancelCellEditing(); } TableColumn resizingColumn = getResizingColumn(); // Need to do this here, before the parent's
--- a/jdk/src/share/classes/javax/swing/ToolTipManager.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/ToolTipManager.java Wed Oct 13 17:51:41 2010 -0700 @@ -459,7 +459,7 @@ if (insideComponent == null) { // Drag exit } - if (window != null && event.getSource() == window) { + if (window != null && event.getSource() == window && insideComponent != null) { // if we get an exit and have a heavy window // we need to check if it if overlapping the inside component Container insideComponentWindow = insideComponent.getTopLevelAncestor();
--- a/jdk/src/share/classes/javax/swing/plaf/LayerUI.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/plaf/LayerUI.java Wed Oct 13 17:51:41 2010 -0700 @@ -600,104 +600,6 @@ } /** - * Returns the preferred size of the viewport for a view component. - * - * @param l the {@code JLayer} component where this UI delegate is being installed - * @return the preferred size of the viewport for a view component - * @see Scrollable#getPreferredScrollableViewportSize() - */ - public Dimension getPreferredScrollableViewportSize(JLayer<? extends V> l) { - if (l.getView() instanceof Scrollable) { - return ((Scrollable)l.getView()).getPreferredScrollableViewportSize(); - } - return l.getPreferredSize(); - } - - /** - * Returns a scroll increment, which is required for components - * that display logical rows or columns in order to completely expose - * one block of rows or columns, depending on the value of orientation. - * - * @param l the {@code JLayer} component where this UI delegate is being installed - * @param visibleRect The view area visible within the viewport - * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL. - * @param direction Less than zero to scroll up/left, greater than zero for down/right. - * @return the "block" increment for scrolling in the specified direction - * @see Scrollable#getScrollableBlockIncrement(Rectangle, int, int) - */ - public int getScrollableBlockIncrement(JLayer<? extends V> l, - Rectangle visibleRect, - int orientation, int direction) { - if (l.getView() instanceof Scrollable) { - return ((Scrollable)l.getView()).getScrollableBlockIncrement( - visibleRect,orientation, direction); - } - return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : - visibleRect.width; - } - - /** - * Returns {@code false} to indicate that the height of the viewport does not - * determine the height of the layer, unless the preferred height - * of the layer is smaller than the height of the viewport. - * - * @param l the {@code JLayer} component where this UI delegate is being installed - * @return whether the layer should track the height of the viewport - * @see Scrollable#getScrollableTracksViewportHeight() - */ - public boolean getScrollableTracksViewportHeight(JLayer<? extends V> l) { - if (l.getView() instanceof Scrollable) { - return ((Scrollable)l.getView()).getScrollableTracksViewportHeight(); - } - return false; - } - - /** - * Returns {@code false} to indicate that the width of the viewport does not - * determine the width of the layer, unless the preferred width - * of the layer is smaller than the width of the viewport. - * - * @param l the {@code JLayer} component where this UI delegate is being installed - * @return whether the layer should track the width of the viewport - * @see Scrollable - * @see LayerUI#getScrollableTracksViewportWidth(JLayer) - */ - public boolean getScrollableTracksViewportWidth(JLayer<? extends V> l) { - if (l.getView() instanceof Scrollable) { - return ((Scrollable)l.getView()).getScrollableTracksViewportWidth(); - } - return false; - } - - /** - * Returns a scroll increment, which is required for components - * that display logical rows or columns in order to completely expose - * one new row or column, depending on the value of orientation. - * Ideally, components should handle a partially exposed row or column - * by returning the distance required to completely expose the item. - * <p> - * Scrolling containers, like JScrollPane, will use this method - * each time the user requests a unit scroll. - * - * @param l the {@code JLayer} component where this UI delegate is being installed - * @param visibleRect The view area visible within the viewport - * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL. - * @param direction Less than zero to scroll up/left, greater than zero for down/right. - * @return The "unit" increment for scrolling in the specified direction. - * This value should always be positive. - * @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int) - */ - public int getScrollableUnitIncrement(JLayer<? extends V> l, - Rectangle visibleRect, - int orientation, int direction) { - if (l.getView() instanceof Scrollable) { - return ((Scrollable)l.getView()).getScrollableUnitIncrement( - visibleRect, orientation, direction); - } - return 1; - } - - /** * If the {@code JLayer}'s view component is not {@code null}, * this calls the view's {@code getBaseline()} method. * Otherwise, the default implementation is called. @@ -718,7 +620,7 @@ /** * If the {@code JLayer}'s view component is not {@code null}, - * this calls the view's {@code getBaselineResizeBehavior()} method. + * this returns the result of the view's {@code getBaselineResizeBehavior()} method. * Otherwise, the default implementation is called. * * @param c {@code JLayer} to return baseline resize behavior for @@ -732,4 +634,90 @@ } return super.getBaselineResizeBehavior(c); } + + /** + * Causes the passed instance of {@code JLayer} to lay out its components. + * + * @param l the {@code JLayer} component where this UI delegate is being installed + */ + public void doLayout(JLayer<? extends V> l) { + Component view = l.getView(); + if (view != null) { + view.setBounds(0, 0, l.getWidth(), l.getHeight()); + } + Component glassPane = l.getGlassPane(); + if (glassPane != null) { + glassPane.setBounds(0, 0, l.getWidth(), l.getHeight()); + } + } + + /** + * If the {@code JLayer}'s view component is not {@code null}, + * this returns the result of the view's {@code getPreferredSize()} method. + * Otherwise, the default implementation is used. + * + * @param c {@code JLayer} to return preferred size for + * @return preferred size for the passed {@code JLayer} + */ + public Dimension getPreferredSize(JComponent c) { + JLayer l = (JLayer) c; + Component view = l.getView(); + if (view != null) { + return view.getPreferredSize(); + } + return super.getPreferredSize(c); + } + + /** + * If the {@code JLayer}'s view component is not {@code null}, + * this returns the result of the view's {@code getMinimalSize()} method. + * Otherwise, the default implementation is used. + * + * @param c {@code JLayer} to return preferred size for + * @return minimal size for the passed {@code JLayer} + */ + public Dimension getMinimumSize(JComponent c) { + JLayer l = (JLayer) c; + Component view = l.getView(); + if (view != null) { + return view.getMinimumSize(); + } + return super.getMinimumSize(c); + } + + /** + * If the {@code JLayer}'s view component is not {@code null}, + * this returns the result of the view's {@code getMaximumSize()} method. + * Otherwise, the default implementation is used. + * + * @param c {@code JLayer} to return preferred size for + * @return maximun size for the passed {@code JLayer} + */ + public Dimension getMaximumSize(JComponent c) { + JLayer l = (JLayer) c; + Component view = l.getView(); + if (view != null) { + return view.getMaximumSize(); + } + return super.getMaximumSize(c); + } + + /** + * Adds the specified region to the dirty region list if the component + * is showing. The component will be repainted after all of the + * currently pending events have been dispatched. + * <p/> + * This method is to be overridden when the dirty region needs to be changed. + * + * @param tm this parameter is not used + * @param x the x value of the dirty region + * @param y the y value of the dirty region + * @param width the width of the dirty region + * @param height the height of the dirty region + * @see java.awt.Component#isShowing + * @see RepaintManager#addDirtyRegion + */ + public void repaint(long tm, int x, int y, int width, int height, JLayer<? extends V> l) { + RepaintManager.currentManager(l).addDirtyRegion(l, x, y, width, height); + } }
--- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicScrollBarUI.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicScrollBarUI.java Wed Oct 13 17:51:41 2010 -0700 @@ -1603,6 +1603,7 @@ BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue(); oldModel.removeChangeListener(modelListener); newModel.addChangeListener(modelListener); + scrollBarValue = scrollbar.getValue(); scrollbar.repaint(); scrollbar.revalidate(); } else if ("orientation" == propertyName) {
--- a/jdk/src/share/classes/javax/swing/plaf/metal/MetalComboBoxUI.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/plaf/metal/MetalComboBoxUI.java Wed Oct 13 17:51:41 2010 -0700 @@ -144,7 +144,7 @@ */ public int getBaseline(JComponent c, int width, int height) { int baseline; - if (MetalLookAndFeel.usingOcean()) { + if (MetalLookAndFeel.usingOcean() && height >= 4) { height -= 4; baseline = super.getBaseline(c, width, height); if (baseline >= 0) {
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Wed Oct 13 17:51:41 2010 -0700 @@ -115,6 +115,9 @@ return new SynthTabbedPaneUI(); } + private SynthTabbedPaneUI() { + } + private boolean scrollableTabLayoutEnabled() { return (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT); }
--- a/jdk/src/share/classes/sun/awt/AWTAccessor.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/sun/awt/AWTAccessor.java Wed Oct 13 17:51:41 2010 -0700 @@ -344,6 +344,11 @@ * Removes the last focus request for the heavyweight from the queue. */ void removeLastFocusRequest(Component heavyweight); + + /* + * Sets the most recent focus owner in the window. + */ + void setMostRecentFocusOwner(Window window, Component component); } /*
--- a/jdk/src/share/classes/sun/awt/EmbeddedFrame.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/sun/awt/EmbeddedFrame.java Wed Oct 13 17:51:41 2010 -0700 @@ -70,7 +70,10 @@ // JDK 1.1 compatibility private static final long serialVersionUID = 2967042741780317130L; - // Use these in traverseOut method to determine directions + /* + * The constants define focus traversal directions. + * Use them in {@code traverseIn}, {@code traverseOut} methods. + */ protected static final boolean FORWARD = true; protected static final boolean BACKWARD = false; @@ -284,6 +287,41 @@ } /** + * This method is called by the embedder when we should receive focus as element + * of the traversal chain. The method requests focus on: + * 1. the first Component of this EmbeddedFrame if user moves focus forward + * in the focus traversal cycle. + * 2. the last Component of this EmbeddedFrame if user moves focus backward + * in the focus traversal cycle. + * + * The direction parameter specifies which of the two mentioned cases is + * happening. Use FORWARD and BACKWARD constants defined in the EmbeddedFrame class + * to avoid confusing boolean values. + * + * A concrete implementation of this method is defined in the platform-dependent + * subclasses. + * + * @param direction FORWARD or BACKWARD + * @return true, if the EmbeddedFrame wants to get focus, false otherwise. + */ + public boolean traverseIn(boolean direction) { + Component comp = null; + + if (direction == FORWARD) { + comp = getFocusTraversalPolicy().getFirstComponent(this); + } else { + comp = getFocusTraversalPolicy().getLastComponent(this); + } + if (comp != null) { + // comp.requestFocus(); - Leads to a hung. + + AWTAccessor.getKeyboardFocusManagerAccessor().setMostRecentFocusOwner(this, comp); + synthesizeWindowActivation(true); + } + return (null != comp); + } + + /** * This method is called from dispatchKeyEvent in the following two cases: * 1. The focus is on the first Component of this EmbeddedFrame and we are * about to transfer the focus backward.
--- a/jdk/src/share/classes/sun/util/locale/BaseLocale.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/share/classes/sun/util/locale/BaseLocale.java Wed Oct 13 17:51:41 2010 -0700 @@ -64,12 +64,14 @@ public static BaseLocale getInstance(String language, String script, String region, String variant) { // JDK uses deprecated ISO639.1 language codes for he, yi and id - if (AsciiUtil.caseIgnoreMatch(language, "he")) { - language = "iw"; - } else if (AsciiUtil.caseIgnoreMatch(language, "yi")) { - language = "ji"; - } else if (AsciiUtil.caseIgnoreMatch(language, "id")) { - language = "in"; + if (language != null) { + if (AsciiUtil.caseIgnoreMatch(language, "he")) { + language = "iw"; + } else if (AsciiUtil.caseIgnoreMatch(language, "yi")) { + language = "ji"; + } else if (AsciiUtil.caseIgnoreMatch(language, "id")) { + language = "in"; + } } Key key = new Key(language, script, region, variant);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/Demo.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2010 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. + */ + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.net.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + +/* + * ZipFileSystem usage demo + * + * java [-cp .../zipfs.jar:./] Demo action ZipfileName [...] + * + * To deploy the provider, either copy the zipfs.jar into JDK/JRE + * extensions directory or add + * <JDK_HOME>/demo/nio/ZipFileSystem/zipfs.jar + * into your class path as showed above. + * + * @author Xueming Shen + */ + +public class Demo { + + static enum Action { + rename, // <java Demo rename zipfile src dst> + // rename entry src to dst inside zipfile + + movein, // <java Demo movein zipfile src dst> + // move an external src file into zipfile + // as entry dst + + moveout, // <java Demo moveout zipfile src dst> + // move a zipfile entry src out to dst + + copy, // <java Demo copy zipfile src dst> + // copy entry src to dst inside zipfile + + copyin, // <java Demo copyin zipfile src dst> + // copy an external src file into zipfile + // as entry dst + + copyout, // <java Demo copyout zipfile src dst> + // copy zipfile entry src" out to file dst + + zzmove, // <java Demo zzmove zfsrc zfdst path> + // move entry path/dir from zfsrc to zfdst + + zzcopy, // <java Demo zzcopy zfsrc zfdst path> + // copy path from zipfile zfsrc to zipfile + // zfdst + + attrs, // <java Demo attrs zipfile path> + // printout the attributes of entry path + + attrsspace, // <java Demo attrsspace zipfile path> + // printout the storespace attrs of entry path + + setmtime, // <java Demo setmtime zipfile "MM/dd/yy-HH:mm:ss" path...> + // set the lastModifiedTime of entry path + + lsdir, // <java Demo lsdir zipfile dir> + // list dir's direct child files/dirs + + mkdir, // <java Demo mkdir zipfile dir> + + mkdirs, // <java Demo mkdirs zipfile dir> + + rmdirs, // <java Demo rmdirs zipfile dir> + + list, // <java Demo list zipfile [dir]> + // recursively list all entries of dir + // via DirectoryStream + + tlist, // <java Demo tlist zipfile [dir]> + // list with buildDirTree=true + + vlist, // <java Demo vlist zipfile [dir]> + // recursively verbose list all entries of + // dir via DirectoryStream + + walk, // <java Demo walk zipfile [dir]> + // recursively walk all entries of dir + // via Files.walkFileTree + + twalk, // <java Demo twalk zipfile [dir]> + // walk with buildDirTree=true + + extract, // <java Demo extract zipfile file [...]> + + update, // <java Demo extract zipfile file [...]> + + delete, // <java Demo delete zipfile file [...]> + + add, // <java Demo add zipfile file [...]> + + create, // <java Demo create zipfile file [...]> + // create a new zipfile if it doesn't exit + // and then add the file(s) into it. + + attrs2, // <java Demo attrs2 zipfile file [...]> + // test different ways to print attrs + } + + public static void main(String[] args) throws Throwable { + + Action action = Action.valueOf(args[0]);; + Map<String, Object> env = env = new HashMap<String, Object>(); + if (action == Action.create) + env.put("createNew", true); + if (action == Action.tlist || action == Action.twalk) + env.put("buildDirTree", true); + + FileSystem fs = FileSystems.newFileSystem( + URI.create("zip" + Paths.get(args[1]).toUri().toString().substring(4)), + env, + null); + try { + FileSystem fs2; + Path path, src, dst; + boolean isRename = false; + switch (action) { + case rename: + src = fs.getPath(args[2]); + dst = fs.getPath(args[3]); + src.moveTo(dst); + break; + case moveout: + src = fs.getPath(args[2]); + dst = Paths.get(args[3]); + src.moveTo(dst); + break; + case movein: + src = Paths.get(args[2]); + dst = fs.getPath(args[3]); + src.moveTo(dst); + break; + case copy: + src = fs.getPath(args[2]); + dst = fs.getPath(args[3]); + src.copyTo(dst); + break; + case copyout: + src = fs.getPath(args[2]); + dst = Paths.get(args[3]); + src.copyTo(dst); + break; + case copyin: + src = Paths.get(args[2]); + dst = fs.getPath(args[3]); + src.copyTo(dst); + break; + case zzmove: + fs2 = FileSystems.newFileSystem( + URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)), + env, + null); + //sf1.getPath(args[3]).moveTo(fs2.getPath(args[3])); + z2zmove(fs, fs2, args[3]); + fs2.close(); + break; + case zzcopy: + fs2 = FileSystems.newFileSystem( + URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)), + env, + null); + //sf1.getPath(args[3]).copyTo(fs2.getPath(args[3])); + z2zcopy(fs, fs2, args[3]); + fs2.close(); + break; + case attrs: + for (int i = 2; i < args.length; i++) { + path = fs.getPath(args[i]); + System.out.println( + Attributes.readBasicFileAttributes(path).toString()); + } + break; + case setmtime: + DateFormat df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss"); + Date newDatetime = df.parse(args[2]); + for (int i = 3; i < args.length; i++) { + path = fs.getPath(args[i]); + path.setAttribute("lastModifiedTime", + FileTime.fromMillis(newDatetime.getTime())); + System.out.println( + Attributes.readBasicFileAttributes(path).toString()); + } + break; + case attrsspace: + path = fs.getPath("/"); + FileStore fstore = path.getFileStore(); + //System.out.println(fstore.getFileStoreAttributeView(FileStoreSpaceAttributeView.class) + // .readAttributes()); + // or + System.out.printf("filestore[%s]%n", fstore.name()); + System.out.printf(" totalSpace: %d%n", + (Long)fstore.getAttribute("space:totalSpace")); + System.out.printf(" usableSpace: %d%n", + (Long)fstore.getAttribute("space:usableSpace")); + System.out.printf(" unallocSpace: %d%n", + (Long)fstore.getAttribute("space:unallocatedSpace")); + break; + case list: + case tlist: + if (args.length < 3) + list(fs.getPath("/"), false); + else + list(fs.getPath(args[2]), false); + break; + case vlist: + if (args.length < 3) + list(fs.getPath("/"), true); + else + list(fs.getPath(args[2]), true); + break; + case twalk: + case walk: + walk(fs.getPath((args.length > 2)? args[2] : "/")); + break; + case extract: + if (args.length == 2) { + extract(fs, "/"); + } else { + for (int i = 2; i < args.length; i++) { + extract(fs, args[i]); + } + } + break; + case delete: + for (int i = 2; i < args.length; i++) + fs.getPath(args[i]).delete(); + break; + case create: + case add: + case update: + for (int i = 2; i < args.length; i++) { + update(fs, args[i]); + } + break; + case lsdir: + path = fs.getPath(args[2]); + final String fStr = (args.length > 3)?args[3]:""; + DirectoryStream<Path> ds = path.newDirectoryStream( + new DirectoryStream.Filter<Path>() { + public boolean accept(Path path) { + return path.toString().contains(fStr); + } + }); + for (Path p : ds) + System.out.println(p); + break; + case mkdir: + fs.getPath(args[2]).createDirectory(); + break; + case mkdirs: + mkdirs(fs.getPath(args[2])); + break; + case attrs2: + for (int i = 2; i < args.length; i++) { + path = fs.getPath(args[i]); + System.out.println("-------(1)---------"); + System.out.println( + Attributes.readBasicFileAttributes(path).toString()); + System.out.println("-------(2)---------"); + Map<String, ?> map = path.readAttributes("zip:*"); + for (Map.Entry<String, ?> e : map.entrySet()) { + System.out.printf(" %s : %s%n", e.getKey(), e.getValue()); + } + System.out.println("-------(3)---------"); + map = path.readAttributes("size,lastModifiedTime,isDirectory"); + for (Map.Entry<String, ?> e : map.entrySet()) { + System.out.printf(" %s : %s%n", e.getKey(), e.getValue()); + } + } + break; + } + } catch (Exception x) { + x.printStackTrace(); + } finally { + if (fs != null) + fs.close(); + } + } + + private static byte[] getBytes(String name) { + return name.getBytes(); + } + + private static String getString(byte[] name) { + return new String(name); + } + + private static void walk(Path path) throws IOException + { + Files.walkFileTree( + path, + new SimpleFileVisitor<Path>() { + private int indent = 0; + private void indent() { + int n = 0; + while (n++ < indent) + System.out.printf(" "); + } + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) + { + indent(); + System.out.printf("%s%n", file.getName().toString()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) + { + indent(); + System.out.printf("[%s]%n", dir.toString()); + indent += 2; + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, + IOException ioe) + { + indent -= 2; + return FileVisitResult.CONTINUE; + } + }); + } + + private static void update(FileSystem fs, String path) throws Throwable{ + Path src = FileSystems.getDefault().getPath(path); + if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) { + DirectoryStream<Path> ds = src.newDirectoryStream(); + for (Path child : ds) + update(fs, child.toString()); + ds.close(); + } else { + Path dst = fs.getPath(path); + Path parent = dst.getParent(); + if (parent != null && parent.notExists()) + mkdirs(parent); + src.copyTo(dst, REPLACE_EXISTING); + } + } + + private static void extract(FileSystem fs, String path) throws Throwable{ + Path src = fs.getPath(path); + if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) { + DirectoryStream<Path> ds = src.newDirectoryStream(); + for (Path child : ds) + extract(fs, child.toString()); + ds.close(); + } else { + if (path.startsWith("/")) + path = path.substring(1); + Path dst = FileSystems.getDefault().getPath(path); + Path parent = dst.getParent(); + if (parent.notExists()) + mkdirs(parent); + src.copyTo(dst, REPLACE_EXISTING); + } + } + + // use DirectoryStream + private static void z2zcopy(FileSystem src, FileSystem dst, String path) + throws IOException + { + Path srcPath = src.getPath(path); + Path dstPath = dst.getPath(path); + + if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) { + if (!dstPath.exists()) { + try { + mkdirs(dstPath); + } catch (FileAlreadyExistsException x) {} + } + DirectoryStream<Path> ds = srcPath.newDirectoryStream(); + for (Path child : ds) { + z2zcopy(src, dst, + path + (path.endsWith("/")?"":"/") + child.getName()); + } + ds.close(); + } else { + //System.out.println("copying..." + path); + srcPath.copyTo(dstPath); + } + } + + // use TreeWalk to move + private static void z2zmove(FileSystem src, FileSystem dst, String path) + throws IOException + { + final Path srcPath = src.getPath(path).toAbsolutePath(); + final Path dstPath = dst.getPath(path).toAbsolutePath(); + + Files.walkFileTree(srcPath, new SimpleFileVisitor<Path>() { + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) + { + Path dst = srcPath.relativize(file); + dst = dstPath.resolve(dst); + try { + Path parent = dstPath.getParent(); + if (parent != null && parent.notExists()) + mkdirs(parent); + file.moveTo(dst); + } catch (IOException x) { + x.printStackTrace(); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) + { + Path dst = srcPath.relativize(dir); + dst = dstPath.resolve(dst); + try { + + if (dst.notExists()) + mkdirs(dst); + } catch (IOException x) { + x.printStackTrace(); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, + IOException ioe) + throws IOException + { + try { + dir.delete(); + } catch (IOException x) { + //x.printStackTrace(); + } + return FileVisitResult.CONTINUE; + } + }); + + } + + private static void mkdirs(Path path) throws IOException { + path = path.toAbsolutePath(); + Path parent = path.getParent(); + if (parent != null) { + if (parent.notExists()) + mkdirs(parent); + } + path.createDirectory(); + } + + private static void rmdirs(Path path) throws IOException { + while (path != null && path.getNameCount() != 0) { + path.delete(); + path = path.getParent(); + } + } + + private static void list(Path path, boolean verbose ) throws IOException { + if (verbose) + System.out.println(Attributes.readBasicFileAttributes(path).toString()); + else + System.out.printf(" %s%n", path.toString()); + if (path.notExists()) + return; + if (Attributes.readBasicFileAttributes(path).isDirectory()) { + DirectoryStream<Path> ds = path.newDirectoryStream(); + for (Path child : ds) + list(child, verbose); + ds.close(); + } + } + + // check the content of two paths are equal + private static void checkEqual(Path src, Path dst) throws IOException + { + //System.out.printf("checking <%s> vs <%s>...%n", + // src.toString(), dst.toString()); + + //streams + InputStream isSrc = src.newInputStream(); + InputStream isDst = dst.newInputStream(); + byte[] bufSrc = new byte[8192]; + byte[] bufDst = new byte[8192]; + + try { + int nSrc = 0; + while ((nSrc = isSrc.read(bufSrc)) != -1) { + int nDst = 0; + while (nDst < nSrc) { + int n = isDst.read(bufDst, nDst, nSrc - nDst); + if (n == -1) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nDst += n; + } + while (--nSrc >= 0) { + if (bufSrc[nSrc] != bufDst[nSrc]) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nSrc--; + } + } + } finally { + isSrc.close(); + isDst.close(); + } + + // channels + SeekableByteChannel chSrc = src.newByteChannel(); + SeekableByteChannel chDst = dst.newByteChannel(); + if (chSrc.size() != chDst.size()) { + System.out.printf("src[%s].size=%d, dst[%s].size=%d%n", + chSrc.toString(), chSrc.size(), + chDst.toString(), chDst.size()); + throw new RuntimeException("CHECK FAILED!"); + } + ByteBuffer bbSrc = ByteBuffer.allocate(8192); + ByteBuffer bbDst = ByteBuffer.allocate(8192); + + try { + int nSrc = 0; + while ((nSrc = chSrc.read(bbSrc)) != -1) { + int nDst = chDst.read(bbDst); + if (nSrc != nDst) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + while (--nSrc >= 0) { + if (bbSrc.get(nSrc) != bbDst.get(nSrc)) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nSrc--; + } + bbSrc.flip(); + bbDst.flip(); + } + } catch (IOException x) { + x.printStackTrace(); + } finally { + chSrc.close(); + chDst.close(); + } + } + + private static void fchCopy(Path src, Path dst) throws IOException + { + Set<OpenOption> read = new HashSet<>(); + read.add(READ); + Set<OpenOption> openwrite = new HashSet<>(); + openwrite.add(CREATE_NEW); + openwrite.add(WRITE); + + FileChannel srcFc = src.getFileSystem() + .provider() + .newFileChannel(src, read); + FileChannel dstFc = dst.getFileSystem() + .provider() + .newFileChannel(dst, openwrite); + + try { + ByteBuffer bb = ByteBuffer.allocate(8192); + while (srcFc.read(bb) >= 0) { + bb.flip(); + dstFc.write(bb); + bb.clear(); + } + } finally { + srcFc.close(); + dstFc.close(); + } + } + + private static void chCopy(Path src, Path dst) throws IOException + { + Set<OpenOption> read = new HashSet<>(); + read.add(READ); + Set<OpenOption> openwrite = new HashSet<>(); + openwrite.add(CREATE_NEW); + openwrite.add(WRITE); + + SeekableByteChannel srcCh = src.newByteChannel(read); + SeekableByteChannel dstCh = dst.newByteChannel(openwrite); + + try { + ByteBuffer bb = ByteBuffer.allocate(8192); + while (srcCh.read(bb) >= 0) { + bb.flip(); + dstCh.write(bb); + bb.clear(); + } + } finally { + srcCh.close(); + dstCh.close(); + } + } + + private static void streamCopy(Path src, Path dst) throws IOException + { + InputStream isSrc = src.newInputStream(); + OutputStream osDst = dst.newOutputStream(); + byte[] buf = new byte[8192]; + try { + int n = 0; + while ((n = isSrc.read(buf)) != -1) { + osDst.write(buf, 0, n); + } + } finally { + isSrc.close(); + osDst.close(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/META-INF/services/java.nio.file.spi.FileSystemProvider Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,3 @@ +com.sun.nio.zipfs.ZipFileSystemProvider +com.sun.nio.zipfs.JarFileSystemProvider +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/README.txt Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,29 @@ +ZipFileSystem is a file system provider that treats the contents of a zip or +JAR file as a java.nio.file.FileSystem. + +To deploy the provider you must copy zipfs.jar into your extensions +directory or else add <JDK_HOME>/demo/nio/ZipFileSystem/zipfs.jar +to your class path. + +The factory methods defined by the java.nio.file.FileSystems class can be +used to create a FileSystem, eg: + + // use file type detection + Map<String,?> env = Collections.emptyMap(); + Path jarfile = Path.get("foo.jar"); + FileSystem fs = FileSystems.newFileSystem(jarfile, env); + +-or + + // locate file system by URI + Map<String,?> env = Collections.emptyMap(); + URI uri = URI.create("zip:///mydir/foo.jar"); + FileSystem fs = FileSystems.newFileSystem(uri, env); + +Once a FileSystem is created then classes in the java.nio.file package +can be used to access files in the zip/JAR file, eg: + + Path mf = fs.getPath("/META-INF/MANIFEST.MF"); + InputStream in = mf.newInputStream(); + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. 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 Sun Microsystems 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. + */ +package com.sun.nio.zipfs; + +import java.nio.file.*; +import java.nio.file.spi.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.FileSystemProvider; + +import java.net.URI; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.channels.FileChannel; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class JarFileSystemProvider extends ZipFileSystemProvider +{ + + @Override + public String getScheme() { + return "jar"; + } + + @Override + protected Path uriToPath(URI uri) { + String scheme = uri.getScheme(); + if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { + throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); + } + try { + String uristr = uri.toString(); + int end = uristr.indexOf("!/"); + uristr = uristr.substring(4, (end == -1) ? uristr.length() : end); + uri = new URI(uristr); + return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null)) + .toAbsolutePath(); + } catch (URISyntaxException e) { + throw new AssertionError(e); //never thrown + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipCoder.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package 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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.nio.ByteBuffer; + +/** + * + * @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; + + /* + * 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 + + + /* + * fields access methods + */ + /////////////////////////////////////////////////////// + static final int CH(byte[] b, int n) { + return b[n] & 0xff; + } + + static final int SH(byte[] b, int n) { + return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); + } + + 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 + + ////////////////////////////////////////// + static final int CH(ByteBuffer b, int pos) { + return b.get(pos) & 0xff; + } + static final int SH(ByteBuffer b, int pos) { + return b.getShort(pos) & 0xffff; + } + static final long LG(ByteBuffer b, int pos) { + return b.getInt(pos) & 0xffffffffL; + } + + // central directory header (END) fields + static final long CENSIG(ByteBuffer b, int pos) { return LG(b, pos + 0); } + static final int CENVEM(ByteBuffer b, int pos) { return SH(b, pos + 4); } + static final int CENVER(ByteBuffer b, int pos) { return SH(b, pos + 6); } + static final int CENFLG(ByteBuffer b, int pos) { return SH(b, pos + 8); } + static final int CENHOW(ByteBuffer b, int pos) { return SH(b, pos + 10);} + static final long CENTIM(ByteBuffer b, int pos) { return LG(b, pos + 12);} + static final long CENCRC(ByteBuffer b, int pos) { return LG(b, pos + 16);} + static final long CENSIZ(ByteBuffer b, int pos) { return LG(b, pos + 20);} + static final long CENLEN(ByteBuffer b, int pos) { return LG(b, pos + 24);} + static final int CENNAM(ByteBuffer b, int pos) { return SH(b, pos + 28);} + static final int CENEXT(ByteBuffer b, int pos) { return SH(b, pos + 30);} + static final int CENCOM(ByteBuffer b, int pos) { return SH(b, pos + 32);} + static final int CENDSK(ByteBuffer b, int pos) { return SH(b, pos + 34);} + static final int CENATT(ByteBuffer b, int pos) { return SH(b, pos + 36);} + static final long CENATX(ByteBuffer b, int pos) { return LG(b, pos + 38);} + static final long CENOFF(ByteBuffer 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.nio.file.DirectoryStream; +import java.nio.file.ClosedDirectoryStreamException; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.io.IOException; +import static com.sun.nio.zipfs.ZipUtils.*; + +/** + * + * @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; + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + + +package com.sun.nio.zipfs; + +import java.nio.file.ReadOnlyFileSystemException; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; +import java.io.IOException; +import java.util.LinkedHashMap; + +/* + * @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); + return; + } catch (IllegalArgumentException x) {} + throw new UnsupportedOperationException("'" + attribute + + "' is unknown or read-only attribute"); + } + + public Object getAttribute(String attribute, boolean domap) + throws IOException + { + ZipFileAttributes zfas = readAttributes(); + if (!domap) { + try { + return attribute(AttrID.valueOf(attribute), zfas); + } catch (IllegalArgumentException x) {} + return null; + } + LinkedHashMap<String, Object> map = new LinkedHashMap<>(); + if ("*".equals(attribute)) { + for (AttrID id : AttrID.values()) { + try { + map.put(id.name(), attribute(id, zfas)); + } catch (IllegalArgumentException x) {} + } + } else { + String[] as = attribute.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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + + +package com.sun.nio.zipfs; + +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Arrays; +import java.util.Formatter; +import static com.sun.nio.zipfs.ZipUtils.*; + +/** + * + * @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(dosToJavaTime(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(dosToJavaTime(e.atime)); + return null; + } + + @Override + public FileTime lastModifiedTime() { + return FileTime.fromMillis(dosToJavaTime(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 byte[] name() { + return Arrays.copyOf(e.name, e.name.length); + } + + 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(); + Formatter fm = new Formatter(sb); + fm.format("[/%s]%n", new String(e.name)); // TBD encoding + fm.format(" creationTime : %s%n", creationTime()); + 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(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileStore.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; +import java.nio.file.attribute.FileStoreSpaceAttributeView; +import java.nio.file.attribute.FileStoreSpaceAttributes; +import java.nio.file.attribute.Attributes; +import java.nio.file.attribute.BasicFileAttributeView; +import java.util.Formatter; + +/* + * + * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal + */ + +public class ZipFileStore extends FileStore { + + private final ZipFileSystem zfs; + + ZipFileStore(ZipPath zpath) { + this.zfs = (ZipFileSystem)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(); + if (type == FileStoreSpaceAttributeView.class) + return (V) new ZipFileStoreAttributeView(this); + return null; + } + + @Override + public Object getAttribute(String attribute) throws IOException { + if (attribute.equals("space:totalSpace")) + return new ZipFileStoreAttributeView(this).readAttributes().totalSpace(); + if (attribute.equals("space:usableSpace")) + return new ZipFileStoreAttributeView(this).readAttributes().usableSpace(); + if (attribute.equals("space:unallocatedSpace")) + return new ZipFileStoreAttributeView(this).readAttributes().unallocatedSpace(); + throw new UnsupportedOperationException("does not support the given attribute"); + } + + private static class ZipFileStoreAttributeView implements FileStoreSpaceAttributeView { + + private final ZipFileStore fileStore; + + public ZipFileStoreAttributeView(ZipFileStore fileStore) { + this.fileStore = fileStore; + } + + @Override + public String name() { + return "space"; + } + + @Override + public FileStoreSpaceAttributes readAttributes() throws IOException { + final String file = fileStore.name(); + Path path = FileSystems.getDefault().getPath(file); + final long size = Attributes.readBasicFileAttributes(path).size(); + final FileStore fstore = path.getFileStore(); + final FileStoreSpaceAttributes fstoreAttrs = + Attributes.readFileStoreSpaceAttributes(fstore); + return new FileStoreSpaceAttributes() { + public long totalSpace() { + return size; + } + + public long usableSpace() { + if (!fstore.isReadOnly()) + return fstoreAttrs.usableSpace(); + return 0; + } + + public long unallocatedSpace() { + if (!fstore.isReadOnly()) + return fstoreAttrs.unallocatedSpace(); + return 0; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + Formatter fm = new Formatter(sb); + fm.format("FileStoreSpaceAttributes[%s]%n", file); + fm.format(" totalSpace: %d%n", totalSpace()); + fm.format(" usableSpace: %d%n", usableSpace()); + fm.format(" unallocSpace: %d%n", unallocatedSpace()); + fm.close(); + return sb.toString(); + } + }; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,2257 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.MappedByteBuffer; +import java.nio.channels.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.*; +import java.net.URI; +import java.util.*; +import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.Inflater; +import java.util.zip.Deflater; +import java.util.zip.InflaterInputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.ZipException; +import java.util.zip.ZipError; +import static java.lang.Boolean.*; +import static com.sun.nio.zipfs.ZipConstants.*; +import static com.sun.nio.zipfs.ZipUtils.*; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + +/** + * A FileSystem built on a zip file + * + * @author Xueming Shen + */ + +public class ZipFileSystem extends FileSystem { + + private final ZipFileSystemProvider provider; + private final ZipPath defaultdir; + private boolean readOnly = false; + private final Path zfpath; + private final ZipCoder zc; + + private final Object lock = new Object(); + + // configurable by env map + private final String defaultDir; // default dir for the file system + private final String nameEncoding; // default encoding for name/comment + private final boolean buildDirTree; // build a dir tree for directoryStream ops + private final boolean useTempFile; // use a temp file for newOS, default + // is to use BAOS for better performance + private final boolean createNew; // create a new zip if not exists + + ZipFileSystem(ZipFileSystemProvider provider, + Path zfpath, + Map<String, ?> env) + throws IOException + { + // configurable env setup + this.buildDirTree = TRUE.equals(env.get("buildDirTree")); + this.useTempFile = TRUE.equals(env.get("useTempFile")); + this.createNew = TRUE.equals(env.get("createNew")); + this.nameEncoding = env.containsKey("nameEncoding") ? + (String)env.get("nameEncoding") : "UTF-8"; + this.defaultDir = env.containsKey("default.dir") ? + (String)env.get("default.dir") : "/"; + if (this.defaultDir.charAt(0) != '/') + throw new IllegalArgumentException("default dir should be absolute"); + + this.provider = provider; + this.zfpath = zfpath; + if (zfpath.notExists()) { + if (createNew) { + OutputStream os = zfpath.newOutputStream(CREATE_NEW, WRITE); + new END().write(os, 0); + os.close(); + } else { + throw new FileSystemNotFoundException(zfpath.toString()); + } + } + zfpath.checkAccess(AccessMode.READ); // sm and existence check + try { + zfpath.checkAccess(AccessMode.WRITE); + } catch (AccessDeniedException x) { + this.readOnly = true; + } + this.zc = ZipCoder.get(nameEncoding); + this.defaultdir = new ZipPath(this, getBytes(defaultDir)); + initZipFile(); + } + + @Override + public FileSystemProvider provider() { + return provider; + } + + @Override + public String getSeparator() { + return "/"; + } + + @Override + public boolean isOpen() { + return isOpen; + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + private void checkWritable() throws IOException { + if (readOnly) + throw new ReadOnlyFileSystemException(); + } + + @Override + public Iterable<Path> getRootDirectories() { + ArrayList<Path> pathArr = new ArrayList<>(); + pathArr.add(new ZipPath(this, new byte[]{'/'})); + return pathArr; + } + + ZipPath getDefaultDir() { // package private + return defaultdir; + } + + @Override + public ZipPath getPath(String path) { + if (path.length() == 0) + throw new InvalidPathException(path, "path should not be empty"); + return new ZipPath(this, getBytes(path)); + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + throw new UnsupportedOperationException(); + } + + @Override + public WatchService newWatchService() { + throw new UnsupportedOperationException(); + } + + FileStore getFileStore(ZipPath path) { + return new ZipFileStore(path); + } + + @Override + public Iterable<FileStore> getFileStores() { + ArrayList<FileStore> list = new ArrayList<FileStore>(1); + list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'}))); + return list; + } + + private static final Set<String> supportedFileAttributeViews = + Collections.unmodifiableSet( + new HashSet<String>(Arrays.asList("basic", "zip"))); + + @Override + public Set<String> supportedFileAttributeViews() { + return supportedFileAttributeViews; + } + + @Override + public String toString() { + return zfpath.toString(); + } + + Path getZipFile() { + return zfpath; + } + + private static final String GLOB_SYNTAX = "glob"; + private static final String REGEX_SYNTAX = "regex"; + + @Override + public PathMatcher getPathMatcher(String syntaxAndInput) { + int pos = syntaxAndInput.indexOf(':'); + if (pos <= 0 || pos == syntaxAndInput.length()) { + throw new IllegalArgumentException(); + } + String syntax = syntaxAndInput.substring(0, pos); + String input = syntaxAndInput.substring(pos + 1); + String expr; + if (syntax.equals(GLOB_SYNTAX)) { + expr = toRegexPattern(input); + } else { + if (syntax.equals(REGEX_SYNTAX)) { + expr = input; + } else { + throw new UnsupportedOperationException("Syntax '" + syntax + + "' not recognized"); + } + } + // return matcher + final Pattern pattern = Pattern.compile(expr); + return new PathMatcher() { + @Override + public boolean matches(Path path) { + return pattern.matcher(path.toString()).matches(); + } + }; + } + + @Override + public void close() throws IOException { + synchronized (lock) { + if (!isOpen) + return; + isOpen = false; + if (!streams.isEmpty()) { + synchronized(streams) { + for (InputStream is: streams) + is.close(); + } + } + sync(); + ch.close(); + } + synchronized (inflaters) { + for (Inflater inf : inflaters) + inf.end(); + } + synchronized (deflaters) { + for (Deflater def : deflaters) + def.end(); + } + for (Path p: tmppaths) { + try { + p.deleteIfExists(); + } catch (IOException x) { + x.printStackTrace(); + } + } + provider.removeFileSystem(zfpath); + } + + ZipFileAttributes[] getAllAttributes() throws IOException { + ensureOpen(); + int n = inodes.size(); + ZipFileAttributes[] zes = new ZipFileAttributes[n]; + Iterator<IndexNode> itr = inodes.values().iterator(); + int i = 0; + while(itr.hasNext()) { + zes[i++] = new ZipFileAttributes(Entry.readCEN(cen, itr.next().pos)); + } + return zes; + } + + EntryName[] getEntryNames() throws IOException { + ensureOpen(); + return inodes.keySet().toArray(new EntryName[0]); + } + + ZipFileAttributes getFileAttributes(byte[] path) + throws IOException + { + synchronized (lock) { + Entry e = getEntry0(path); + if (e == null) { + if (path.length == 0) { + e = new Entry(new byte[0]); // root + } else if (buildDirTree) { + IndexNode inode = getDirs().get(new EntryName(path)); + if (inode == null) + return null; + e = new Entry(inode.name); + } else { + return null; + } + e.method = METHOD_STORED; // STORED for dir + BasicFileAttributes bfas = Attributes.readBasicFileAttributes(zfpath); + if (bfas.lastModifiedTime() != null) + e.mtime = javaToDosTime(bfas.lastModifiedTime().toMillis()); + if (bfas.lastAccessTime() != null) + e.atime = javaToDosTime(bfas.lastAccessTime().toMillis()); + if (bfas.creationTime() != null) + e.ctime = javaToDosTime(bfas.creationTime().toMillis()); + } + return new ZipFileAttributes(e); + } + } + + void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime) + throws IOException + { + checkWritable(); + synchronized (lock) { + Entry e = getEntry0(path); // ensureOpen checked + if (e == null) + throw new NoSuchFileException(getString(path)); + if (e.type == Entry.CEN) + e.type = Entry.COPY; // copy e + if (mtime != null) + e.mtime = javaToDosTime(mtime.toMillis()); + if (atime != null) + e.atime = javaToDosTime(atime.toMillis()); + if (ctime != null) + e.ctime = javaToDosTime(ctime.toMillis()); + update(e); + } + } + + boolean exists(byte[] path) + throws IOException + { + return getEntry0(path) != null; + } + + boolean isDirectory(byte[] path) + throws IOException + { + synchronized (lock) { + if (buildDirTree) { + return getDirs().containsKey(new EntryName(path)); + } + Entry e = getEntry0(path); + return (e != null && e.isDir()) || path.length == 0; + } + } + + private ZipPath toZipPath(byte[] path) { + // make it absolute + byte[] p = new byte[path.length + 1]; + p[0] = '/'; + System.arraycopy(path, 0, p, 1, path.length); + return new ZipPath(this, p); + } + + // returns the list of child paths of "path" + Iterator<Path> iteratorOf(byte[] path, + DirectoryStream.Filter<? super Path> filter) + throws IOException + { + synchronized (lock) { + if (buildDirTree) { + IndexNode inode = getDirs().get(new EntryName(path)); + if (inode == null) + throw new NotDirectoryException(getString(path)); + List<Path> list = new ArrayList<Path>(); + IndexNode child = inode.child; + while (child != null) { + ZipPath zp = toZipPath(child.name); + if (filter == null || filter.accept(zp)) + list.add(zp); + child = child.sibling; + } + return list.iterator(); + } + + if (!isDirectory(path)) + throw new NotDirectoryException(getString(path)); + List<Path> list = new ArrayList<Path>(); + EntryName[] entries = getEntryNames(); + path = toDirectoryPath(path); + for (EntryName en :entries) { + if (!isParentOf(path, en.name)) // is "path" the parent of "name" + continue; + int off = path.length; + while (off < en.name.length) { + if (en.name[off] == '/') + break; + off++; + } + if (off < (en.name.length - 1)) + continue; + ZipPath zp = toZipPath(en.name); + if (filter == null || filter.accept(zp)) + list.add(zp); + } + return list.iterator(); + } + } + + void createDirectory(byte[] dir, FileAttribute<?>... attrs) + throws IOException + { + checkWritable(); + dir = toDirectoryPath(dir); + synchronized (lock) { + ensureOpen(); + // pseudo root dir, or exiting dir + if (dir.length == 0 || exists(dir)) + throw new FileAlreadyExistsException(getString(dir)); + checkParents(dir); + + Entry e = new Entry(dir, Entry.NEW); + e.method = METHOD_STORED; // STORED for dir + update(e); + } + } + + void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options) + throws IOException + { + checkWritable(); + if (Arrays.equals(src, dst)) + return; // do nothing, src and dst are the same + synchronized (lock) { + Entry eSrc = getEntry0(src); // ensureOpen checked + if (eSrc == null) + throw new NoSuchFileException(getString(src)); + if (eSrc.isDir()) { // spec says to create dst dir + createDirectory(dst); + return; + } + boolean hasReplace = false; + boolean hasCopyAttrs = false; + for (CopyOption opt : options) { + if (opt == REPLACE_EXISTING) + hasReplace = true; + else if (opt == COPY_ATTRIBUTES) + hasCopyAttrs = true; + } + Entry eDst = getEntry0(dst); + if (eDst != null) { + if (!hasReplace) + throw new FileAlreadyExistsException(getString(dst)); + } else { + checkParents(dst); + } + Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry + u.name = dst; // change name + // don't touch the "nlen and elen" here. writeLOC() always + // re-calculate from "name" and "extra" for the correct length, + // copyLOCEntry however needs the original lengths to skip the + // loc header. + // u.nlen = dst.length; + if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) + { + u.type = eSrc.type; // make it the same type + if (!deletesrc) { // if it's not "rename", just take the data + if (eSrc.bytes != null) + u.bytes = Arrays.copyOf(eSrc.bytes, eSrc.bytes.length); + else if (eSrc.file != null) { + u.file = getTempPathForEntry(null); + eSrc.file.copyTo(u.file, REPLACE_EXISTING); + } + } + } + if (!hasCopyAttrs) + u.mtime = u.atime= u.ctime = javaToDosTime(System.currentTimeMillis()); + update(u); + if (deletesrc) + updateDelete(eSrc); + } + } + + // Returns an output stream for writing the contents into the specified + // entry. + OutputStream newOutputStream(byte[] path, OpenOption... options) + throws IOException + { + checkWritable(); + boolean hasCreateNew = false; + boolean hasCreate = false; + boolean hasAppend = false; + for (OpenOption opt: options) { + if (opt == READ) + throw new IllegalArgumentException("READ not allowed"); + if (opt == CREATE_NEW) + hasCreateNew = true; + if (opt == CREATE) + hasCreate = true; + if (opt == APPEND) + hasAppend = true; + } + synchronized (lock) { + Entry e = getEntry0(path); + if (e != null) { + if (e.isDir() || hasCreateNew) + throw new FileAlreadyExistsException(getString(path)); + if (hasAppend) { + InputStream is = getInputStream(e); + OutputStream os = getOutputStream(new Entry(e, Entry.NEW)); + copyStream(is, os); + is.close(); + return os; + } + return getOutputStream(new Entry(e, Entry.NEW)); + } else { + if (!hasCreate && !hasCreateNew) + throw new NoSuchFileException(getString(path)); + checkParents(path); + return getOutputStream(new Entry(path, Entry.NEW)); + } + } + } + + // Returns an input stream for reading the contents of the specified + // file entry. + InputStream newInputStream(byte[] path) throws IOException { + synchronized (lock) { + Entry e = getEntry0(path); + if (e == null) + throw new NoSuchFileException(getString(path)); + if (e.isDir()) + throw new FileSystemException(getString(path), "is a directory", null); + return getInputStream(e); + } + } + + private void checkOptions(Set<? extends OpenOption> options) { + // check for options of null type and option is an intance of StandardOpenOption + for (OpenOption option : options) { + if (option == null) + throw new NullPointerException(); + if (!(option instanceof StandardOpenOption)) + throw new IllegalArgumentException(); + } + } + + // Returns a Writable/ReadByteChannel for now. Might consdier to use + // newFileChannel() instead, which dump the entry data into a regular + // file on the default file system and create a FileChannel on top of + // it. + SeekableByteChannel newByteChannel(byte[] path, + Set<? extends OpenOption> options, + FileAttribute<?>... attrs) + throws IOException + { + checkOptions(options); + if (options.contains(StandardOpenOption.WRITE) || + options.contains(StandardOpenOption.APPEND)) { + checkWritable(); + final WritableByteChannel wbc = Channels.newChannel(newOutputStream(path, + options.toArray(new OpenOption[0]))); + long leftover = 0;; + if (options.contains(StandardOpenOption.APPEND)) { + Entry e = getEntry0(path); + if (e != null && e.size >= 0) + leftover = e.size; + } + final long offset = leftover; + return new SeekableByteChannel() { + long written = offset; + public boolean isOpen() { + return wbc.isOpen(); + } + public long position() throws IOException { + return written; + } + public SeekableByteChannel position(long pos) throws IOException { + throw new UnsupportedOperationException(); + } + public int read(ByteBuffer dst) throws IOException { + throw new UnsupportedOperationException(); + } + public SeekableByteChannel truncate(long size) throws IOException { + throw new UnsupportedOperationException(); + } + public int write(ByteBuffer src) throws IOException { + int n = wbc.write(src); + written += n; + return n; + } + public long size() throws IOException { + return written; + } + public void close() throws IOException { + wbc.close(); + } + }; + } else { + Entry e = getEntry0(path); + if (e == null || e.isDir()) + throw new NoSuchFileException(getString(path)); + final ReadableByteChannel rbc = + Channels.newChannel(getInputStream(e)); + final long size = e.size; + return new SeekableByteChannel() { + long read = 0; + public boolean isOpen() { + return rbc.isOpen(); + } + public long position() throws IOException { + return read; + } + public SeekableByteChannel position(long pos) throws IOException { + throw new UnsupportedOperationException(); + } + public int read(ByteBuffer dst) throws IOException { + return rbc.read(dst); + } + public SeekableByteChannel truncate(long size) throws IOException { + throw new NonWritableChannelException(); + } + public int write (ByteBuffer src) throws IOException { + throw new NonWritableChannelException(); + } + public long size() throws IOException { + return size; + } + public void close() throws IOException { + rbc.close(); + } + }; + } + } + + // Returns a FileChannel of the specified entry. + // + // This implementation creates a temporary file on the default file system, + // copy the entry data into it if the entry exists, and then create a + // FileChannel on top of it. + FileChannel newFileChannel(byte[] path, + Set<? extends OpenOption> options, + FileAttribute<?>... attrs) + throws IOException + { + checkOptions(options); + final boolean forWrite = (options.contains(StandardOpenOption.WRITE) || + options.contains(StandardOpenOption.APPEND)); + Entry e = getEntry0(path); + if (forWrite) { + checkWritable(); + if (e == null) { + if (!options.contains(StandardOpenOption.CREATE_NEW)) + throw new NoSuchFileException(getString(path)); + } else { + if (options.contains(StandardOpenOption.CREATE_NEW)) + throw new FileAlreadyExistsException(getString(path)); + if (e.isDir()) + throw new FileAlreadyExistsException("directory <" + + getString(path) + "> exists"); + } + options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile + } else if (e == null || e.isDir()) { + throw new NoSuchFileException(getString(path)); + } + + final boolean isFCH = (e != null && e.type == Entry.FILECH); + final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path); + final FileChannel fch = tmpfile.getFileSystem() + .provider() + .newFileChannel(tmpfile, options, attrs); + final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH); + if (forWrite) { + u.flag = FLAG_DATADESCR; + u.method = METHOD_DEFLATED; + } + // is there a better way to hook into the FileChannel's close method? + return new FileChannel() { + public int write(ByteBuffer src) throws IOException { + return fch.write(src); + } + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + return fch.write(srcs, offset, length); + } + public long position() throws IOException { + return fch.position(); + } + public FileChannel position(long newPosition) + throws IOException + { + fch.position(newPosition); + return this; + } + public long size() throws IOException { + return fch.size(); + } + public FileChannel truncate(long size) + throws IOException + { + fch.truncate(size); + return this; + } + public void force(boolean metaData) + throws IOException + { + fch.force(metaData); + } + public long transferTo(long position, long count, + WritableByteChannel target) + throws IOException + { + return fch.transferTo(position, count, target); + } + public long transferFrom(ReadableByteChannel src, + long position, long count) + throws IOException + { + return fch.transferFrom(src, position, count); + } + public int read(ByteBuffer dst) throws IOException { + return fch.read(dst); + } + public int read(ByteBuffer dst, long position) + throws IOException + { + return fch.read(dst, position); + } + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + return fch.read(dsts, offset, length); + } + public int write(ByteBuffer src, long position) + throws IOException + { + return fch.write(src, position); + } + public MappedByteBuffer map(MapMode mode, + long position, long size) + throws IOException + { + throw new UnsupportedOperationException(); + } + public FileLock lock(long position, long size, boolean shared) + throws IOException + { + return fch.lock(position, size, shared); + } + public FileLock tryLock(long position, long size, boolean shared) + throws IOException + { + return fch.tryLock(position, size, shared); + } + protected void implCloseChannel() throws IOException { + fch.close(); + if (forWrite) { + u.mtime = javaToDosTime(System.currentTimeMillis()); + u.size = Attributes.readBasicFileAttributes(u.file).size(); + update(u); + } else { + if (!isFCH) // if this is a new fch for reading + removeTempPathForEntry(tmpfile); + } + } + }; + } + + // the outstanding input streams that need to be closed + private Set<InputStream> streams = + Collections.synchronizedSet(new HashSet<InputStream>()); + + // the ex-channel and ex-path that need to close when their outstanding + // input streams are all closed by the obtainers. + private Set<ExChannelCloser> exChClosers = new HashSet<>(); + + private Set<Path> tmppaths = new HashSet<>(); + private Path getTempPathForEntry(byte[] path) throws IOException { + Path tmpPath = createTempFileInSameDirectoryAs(zfpath); + tmppaths.add(tmpPath); + + if (path != null) { + Entry e = getEntry0(path); + if (e != null) { + InputStream is = newInputStream(path); + OutputStream os = tmpPath.newOutputStream(WRITE); + try { + copyStream(is, os); + } finally { + is.close(); + os.close(); + } + } + } + return tmpPath; + } + + private void removeTempPathForEntry(Path path) throws IOException { + path.delete(); + tmppaths.remove(path); + } + + // check if all parents really exit. ZIP spec does not require + // the existence of any "parent directory". + private void checkParents(byte[] path) throws IOException { + while ((path = getParent(path)) != null) { + if (!inodes.containsKey(new EntryName(path))) + throw new NoSuchFileException(getString(path)); + } + } + + private static byte[] getParent(byte[] path) { + int off = path.length - 1; + if (off > 0 && path[off] == '/') // isDirectory + off--; + while (off > 0 && path[off] != '/') { off--; } + if (off == 0) + return null; // top entry + return Arrays.copyOf(path, off + 1); + } + + // If "starter" is the parent directory of "path" + private static boolean isParentOf(byte[] p, byte[] c) { + final int plen = p.length; + if (plen == 0) // root dir + return true; + if (plen >= c.length) + return false; + int n = 0; + while (n < plen) { + if (p[n] != c[n]) + return false; + n++; + } + if (p[n - 1] != '/' && (c[n] != '/' || n == c.length - 1)) + return false; + return true; + } + + /////////////////////////////////////////////////////////////////// + private void initZipFile() throws IOException { + ch = zfpath.newByteChannel(READ); + initCEN(); + } + + private volatile boolean isOpen = true; + private SeekableByteChannel ch; // channel to the zipfile + ByteBuffer cen; // CEN & ENDHDR + private END end; + private long locpos; // position of first LOC header (usually 0) + + // name -> pos (in cen), package private for ZipInfo + LinkedHashMap<EntryName, IndexNode> inodes; + + byte[] getBytes(String name) { + return zc.getBytes(name); + } + String getString(byte[] name) { + return zc.toString(name); + } + + protected void finalize() throws IOException { + close(); + } + + private long getDataPos(Entry e) throws IOException { + if (e.locoff == -1) { + Entry e2 = getEntry0(e.name); + if (e2 == null) + throw new ZipException("invalid loc for entry <" + e.name + ">"); + e.locoff = e2.locoff; + } + byte[] buf = new byte[LOCHDR]; + if (readFullyAt(buf, 0, buf.length, e.locoff) != buf.length) + throw new ZipException("invalid loc for entry <" + e.name + ">"); + return locpos + e.locoff + LOCHDR + LOCNAM(buf) + LOCEXT(buf); + } + + // Reads len bytes of data from the specified offset into buf. + // Returns the total number of bytes read. + // Each/every byte read from here (except the cen, which is mapped). + private long readFullyAt(byte[] buf, int off, long len, long pos) + throws IOException + { + ByteBuffer bb = ByteBuffer.wrap(buf); + bb.position(off); + bb.limit((int)(off + len)); + return readFullyAt(bb, pos); + } + + private long readFullyAt(ByteBuffer bb, long pos) + throws IOException + { + synchronized(ch) { + return ch.position(pos).read(bb); + } + } + + // Searches for end of central directory (END) header. The contents of + // the END header will be read and placed in endbuf. Returns the file + // position of the END header, otherwise returns -1 if the END header + // was not found or an error occurred. + private END findEND() throws IOException + { + byte[] buf = new byte[READBLOCKSZ]; + long ziplen = ch.size(); + long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; + long minPos = minHDR - (buf.length - ENDHDR); + + for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) + { + int off = 0; + if (pos < 0) { + // Pretend there are some NUL bytes before start of file + off = (int)-pos; + Arrays.fill(buf, 0, off, (byte)0); + } + int len = buf.length - off; + if (readFullyAt(buf, off, len, pos + off) != len) + zerror("zip END header not found"); + + // Now scan the block backwards for END header signature + for (int i = buf.length - ENDHDR; i >= 0; i--) { + if (buf[i+0] == (byte)'P' && + buf[i+1] == (byte)'K' && + buf[i+2] == (byte)'\005' && + buf[i+3] == (byte)'\006' && + (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) { + // Found END header + buf = Arrays.copyOfRange(buf, i, i + ENDHDR); + END end = new END(); + end.endsub = ENDSUB(buf); + end.centot = ENDTOT(buf); + end.cenlen = ENDSIZ(buf); + end.cenoff = ENDOFF(buf); + end.comlen = ENDCOM(buf); + end.endpos = pos + i; + if (end.cenlen == ZIP64_MINVAL || + end.cenoff == ZIP64_MINVAL || + end.centot == ZIP64_MINVAL32) + { + // need to find the zip64 end; + byte[] loc64 = new byte[ZIP64_LOCHDR]; + if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) + != loc64.length) { + return end; + } + long end64pos = ZIP64_LOCOFF(loc64); + byte[] end64buf = new byte[ZIP64_ENDHDR]; + if (readFullyAt(end64buf, 0, end64buf.length, end64pos) + != end64buf.length) { + return end; + } + // end64 found, re-calcualte everything. + end.cenlen = ZIP64_ENDSIZ(end64buf); + end.cenoff = ZIP64_ENDOFF(end64buf); + end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g + end.endpos = end64pos; + } + return end; + } + } + } + zerror("zip END header not found"); + return null; //make compiler happy + } + + // Reads zip file central directory. Returns the file position of first + // CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL + // then the error was a zip format error and zip->msg has the error text. + // Always pass in -1 for knownTotal; it's used for a recursive call. + private long initCEN() throws IOException { + end = findEND(); + if (end.endpos == 0) { + inodes = new LinkedHashMap<EntryName, IndexNode>(10); + locpos = 0; + return 0; // only END header present + } + if (end.cenlen > end.endpos) + zerror("invalid END header (bad central directory size)"); + long cenpos = end.endpos - end.cenlen; // position of CEN table + + // Get position of first local file (LOC) header, taking into + // account that there may be a stub prefixed to the zip file. + locpos = cenpos - end.cenoff; + if (locpos < 0) + zerror("invalid END header (bad central directory offset)"); + + // read in the CEN and END + cen = ByteBuffer.allocate((int)(end.cenlen + ENDHDR)); + if (readFullyAt(cen, cenpos) != end.cenlen + ENDHDR) { + zerror("read CEN tables failed"); + } + cen.order(ByteOrder.LITTLE_ENDIAN).flip(); + + // Iterate through the entries in the central directory + inodes = new LinkedHashMap<EntryName, IndexNode>(end.centot + 1); + int pos = 0; + int limit = cen.remaining() - ENDHDR; + int i = 0; + byte[] bBuf = new byte[1024]; + while (pos < limit) { + if (CENSIG(cen, pos) != CENSIG) + zerror("invalid CEN header (bad signature)"); + int method = CENHOW(cen, pos); + int nlen = CENNAM(cen, pos); + int elen = CENEXT(cen, pos); + int clen = CENCOM(cen, pos); + if ((CENFLG(cen, pos) & 1) != 0) + zerror("invalid CEN header (encrypted entry)"); + if (method != METHOD_STORED && method != METHOD_DEFLATED) + zerror("invalid CEN header (bad compression method: " + method + ")"); + if (pos + CENHDR + nlen > limit) + zerror("invalid CEN header (bad header size)"); + if (bBuf.length < nlen) + bBuf = new byte[nlen]; + cen.position(pos + CENHDR); + byte[] name = new byte[nlen]; + cen.get(name); + inodes.put(new EntryName(name), new IndexNode(name, pos)); + // skip ext and comment + cen.position(pos += (CENHDR + nlen + elen + clen)); + i++; + } + if (cen.remaining() != ENDHDR) { + zerror("invalid CEN header (bad header size)"); + } + dirs = null; // clear the dir map + return cenpos; + } + + private void ensureOpen() throws IOException { + if (!isOpen) + throw new ClosedFileSystemException(); + } + + // Creates a new empty temporary file in the same directory as the + // specified file. A variant of File.createTempFile. + private static Path createTempFileInSameDirectoryAs(Path path) + throws IOException + { + Path parent = path.toAbsolutePath().getParent(); + String dir = (parent == null)? "." : parent.toString(); + return File.createTempFile("zipfstmp", null, new File(dir)).toPath(); + } + + ////////////////////update & sync ////////////////////////////////////// + + private boolean hasUpdate = false; + private void updateDelete(Entry e) { + EntryName en = new EntryName(e.name); + inodes.remove(en); + hasUpdate = true; + } + + private void update(Entry e) { + EntryName en = new EntryName(e.name); + inodes.put(en, e); + hasUpdate = true; + } + + // copy over the whole LOC entry (header if necessary, data and ext) from + // old zip to the new one. + private long copyLOCEntry(Entry e, boolean updateHeader, + OutputStream os, + long written, byte[] buf) + throws IOException + { + long locoff = e.locoff; // where to read + e.locoff = written; // update the e.locoff with new value + + // calculate the size need to write out + long size = 0; + // if there is A ext + if ((e.flag & FLAG_DATADESCR) != 0) { + if (e.size >= ZIP64_MINVAL || e.csize >= ZIP64_MINVAL) + size = 24; + else + size = 16; + } + if (updateHeader) { // if we need update the loc header + locoff += LOCHDR + e.nlen + e.elen; // skip header + size += e.csize; + written = e.writeLOC(os) + size; + } else { + size += LOCHDR + e.nlen + e.elen + e.csize; + written = size; + } + int n; + while (size > 0 && + (n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1) + { + if (size < n) + n = (int)size; + os.write(buf, 0, n); + size -= n; + locoff += n; + } + return written; + } + + // sync the zip file system, if there is any udpate + private void sync() throws IOException { + assert Thread.holdsLock(this); + + // check ex-closer + if (!exChClosers.isEmpty()) { + for (ExChannelCloser ecc : exChClosers) { + if (ecc.streams.isEmpty()) { + ecc.ch.close(); + ecc.path.delete(); + exChClosers.remove(ecc); + } + } + } + if (!hasUpdate) + return; + + Path tmpFile = createTempFileInSameDirectoryAs(zfpath); + OutputStream os = tmpFile.newOutputStream(WRITE); + ArrayList<Entry> elist = new ArrayList<>(inodes.size()); + long written = 0; + byte[] buf = new byte[8192]; + Entry e = null; + + // write loc + for (IndexNode inode : inodes.values()) { + if (inode instanceof Entry) { // an updated inode + e = (Entry)inode; + try { + if (e.type == Entry.COPY) { + // entry copy: the only thing changed is the "name" + // and "nlen" in LOC header, so we udpate/rewrite the + // LOC in new file and simply copy the rest (data and + // ext) without enflating/deflating from the old zip + // file LOC entry. + written += copyLOCEntry(e, true, os, written, buf); + } else { // NEW or FILECH + e.locoff = written; + written += e.writeLOC(os); // write loc header + if (e.bytes != null) { // in-memory, deflated + os.write(e.bytes); // already + written += e.bytes.length; + } else if (e.file != null) { // tmp file + InputStream is = e.file.newInputStream(); + int n; + if (e.type == Entry.NEW) { // deflated already + while ((n = is.read(buf)) != -1) { + os.write(buf, 0, n); + written += n; + } + } else if (e.type == Entry.FILECH) { + // the data are not deflated, use ZEOS + OutputStream os2 = new EntryOutputStream(e, os); + while ((n = is.read(buf)) != -1) { + os2.write(buf, 0, n); + } + os2.close(); + written += e.csize; + if ((e.flag & FLAG_DATADESCR) != 0) + written += e.writeEXT(os); + } + is.close(); + e.file.delete(); + tmppaths.remove(e.file); + } else { + // dir, 0-length data + } + } + elist.add(e); + } catch (IOException x) { + x.printStackTrace(); // skip any in-accurate entry + } + } else { // unchanged inode + e = Entry.readCEN(cen, inode.pos); + try { + written += copyLOCEntry(e, false, os, written, buf); + elist.add(e); + } catch (IOException x) { + x.printStackTrace(); // skip any wrong entry + } + } + } + + // now write back the cen and end table + end.cenoff = written; + for (Entry entry : elist) { + written += entry.writeCEN(os); + } + end.centot = elist.size(); + end.cenlen = written - end.cenoff; + end.write(os, written); + os.close(); + + if (!streams.isEmpty()) { + // There are outstanding input streams open on existing "ch", + // so, don't close the "cha" and delete the "file for now, let + // the "ex-channel-closer" to handle them + ExChannelCloser ecc = new ExChannelCloser( + createTempFileInSameDirectoryAs(zfpath), + ch, + streams); + zfpath.moveTo(ecc.path, REPLACE_EXISTING); + exChClosers.add(ecc); + streams = Collections.synchronizedSet(new HashSet<InputStream>()); + } else { + ch.close(); + zfpath.delete(); + } + tmpFile.moveTo(zfpath, REPLACE_EXISTING); + hasUpdate = false; // clear + + if (isOpen) { + ch = zfpath.newByteChannel(READ); // re-fresh "ch" and "cen" + initCEN(); + } + //System.out.println("->sync() done!"); + } + + private Entry getEntry0(byte[] path) throws IOException { + assert Thread.holdsLock(this); + + if (path == null) + throw new NullPointerException("path"); + if (path.length == 0) + return null; + EntryName en = new EntryName(path); + IndexNode inode = null; + synchronized (lock) { + ensureOpen(); + if ((inode = inodes.get(en)) == null) { + if (path[path.length -1] == '/') // already has a slash + return null; + path = Arrays.copyOf(path, path.length + 1); + path[path.length - 1] = '/'; + en.name(path); + if ((inode = inodes.get(en)) == null) + return null; + } + if (inode instanceof Entry) + return (Entry)inode; + return Entry.readCEN(cen, inode.pos); + } + } + + // Test if the "name" a parent directory of any entry (dir empty) + boolean isAncestor(byte[] name) { + for (Map.Entry<EntryName, IndexNode> entry : inodes.entrySet()) { + byte[] ename = entry.getKey().name; + if (isParentOf(name, ename)) + return true; + } + return false; + } + + public void deleteFile(byte[] path, boolean failIfNotExists) + throws IOException + { + checkWritable(); + synchronized(lock) { + Entry e = getEntry0(path); + if (e == null) { + if (path != null && path.length == 0) + throw new ZipException("root directory </> can't not be delete"); + if (failIfNotExists) + throw new NoSuchFileException(getString(path)); + } else { + if (e.isDir() && isAncestor(path)) + throw new DirectoryNotEmptyException(getString(path)); + updateDelete(e); + } + } + } + + private static void copyStream(InputStream is, OutputStream os) + throws IOException + { + byte[] copyBuf = new byte[8192]; + int n; + while ((n = is.read(copyBuf)) != -1) { + os.write(copyBuf, 0, n); + } + } + + // Returns an out stream for either + // (1) writing the contents of a new entry, if the entry exits, or + // (2) updating/replacing the contents of the specified existing entry. + private OutputStream getOutputStream(Entry e) throws IOException { + + ensureOpen(); + if (e.mtime == -1) + e.mtime = javaToDosTime(System.currentTimeMillis()); + if (e.method == -1) + e.method = METHOD_DEFLATED; // TBD: use default method + // store size, compressed size, and crc-32 in LOC header + e.flag = 0; + if (zc.isUTF8()) + e.flag |= FLAG_EFS; + OutputStream os; + if (useTempFile) { + e.file = getTempPathForEntry(null); + os = e.file.newOutputStream(WRITE); + } else { + os = new ByteArrayOutputStream((e.size > 0)? (int)e.size : 8192); + } + return new EntryOutputStream(e, os); + } + + private InputStream getInputStream(Entry e) + throws IOException + { + InputStream eis = null; + + if (e.type == Entry.NEW) { + if (e.bytes != null) + eis = new ByteArrayInputStream(e.bytes); + else if (e.file != null) + eis = e.file.newInputStream(); + else + throw new ZipException("update entry data is missing"); + } else if (e.type == Entry.FILECH) { + // FILECH result is un-compressed. + eis = e.file.newInputStream(); + // TBD: wrap to hook close() + // streams.add(eis); + return eis; + } else { // untouced CEN or COPY + eis = new EntryInputStream(e, ch); + } + if (e.method == METHOD_DEFLATED) { + // MORE: Compute good size for inflater stream: + long bufSize = e.size + 2; // Inflater likes a bit of slack + if (bufSize > 65536) + bufSize = 8192; + final long size = e.size;; + eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) { + + private boolean isClosed = false; + public void close() throws IOException { + if (!isClosed) { + releaseInflater(inf); + this.in.close(); + isClosed = true; + } + } + // Override fill() method to provide an extra "dummy" byte + // at the end of the input stream. This is required when + // using the "nowrap" Inflater option. (it appears the new + // zlib in 7 does not need it, but keep it for now) + protected void fill() throws IOException { + if (eof) { + throw new EOFException( + "Unexpected end of ZLIB input stream"); + } + len = this.in.read(buf, 0, buf.length); + if (len == -1) { + buf[0] = 0; + len = 1; + eof = true; + } + inf.setInput(buf, 0, len); + } + private boolean eof; + + public int available() throws IOException { + if (isClosed) + return 0; + long avail = size - inf.getBytesWritten(); + return avail > (long) Integer.MAX_VALUE ? + Integer.MAX_VALUE : (int) avail; + } + }; + } else if (e.method != METHOD_STORED) { + throw new ZipException("invalid compression method"); + } + streams.add(eis); + return eis; + } + + // Inner class implementing the input stream used to read + // a (possibly compressed) zip file entry. + private class EntryInputStream extends InputStream { + private SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might + // point to a new channel after sync() + private long pos; // current position within entry data + protected long rem; // number of remaining bytes within entry + protected long size; // uncompressed size of this entry + + EntryInputStream(Entry e, SeekableByteChannel zfch) + throws IOException + { + this.zfch = zfch; + rem = e.csize; + size = e.size; + pos = getDataPos(e); + } + public int read(byte b[], int off, int len) throws IOException { + ensureOpen(); + if (rem == 0) { + return -1; + } + if (len <= 0) { + return 0; + } + if (len > rem) { + len = (int) rem; + } + // readFullyAt() + long n = 0; + ByteBuffer bb = ByteBuffer.wrap(b); + bb.position(off); + bb.limit(off + len); + synchronized(zfch) { + n = zfch.position(pos).read(bb); + } + if (n > 0) { + pos += n; + rem -= n; + } + if (rem == 0) { + close(); + } + return (int)n; + } + public int read() throws IOException { + byte[] b = new byte[1]; + if (read(b, 0, 1) == 1) { + return b[0] & 0xff; + } else { + return -1; + } + } + public long skip(long n) throws IOException { + ensureOpen(); + if (n > rem) + n = rem; + pos += n; + rem -= n; + if (rem == 0) { + close(); + } + return n; + } + public int available() { + return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; + } + + public long size() { + return size; + } + public void close() { + rem = 0; + streams.remove(this); + } + } + + class EntryOutputStream extends DeflaterOutputStream + { + private CRC32 crc; + private Entry e; + private long written; + + EntryOutputStream(Entry e, OutputStream os) + throws IOException + { + super(os, getDeflater()); + if (e == null) + throw new NullPointerException("Zip entry is null"); + this.e = e; + crc = new CRC32(); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (e.type != Entry.FILECH) // only from sync + ensureOpen(); + if (off < 0 || len < 0 || off > b.length - len) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + switch (e.method) { + case METHOD_DEFLATED: + super.write(b, off, len); + break; + case METHOD_STORED: + written += len; + out.write(b, off, len); + break; + default: + throw new ZipException("invalid compression method"); + } + crc.update(b, off, len); + } + + @Override + public void close() throws IOException { + // TBD ensureOpen(); + switch (e.method) { + case METHOD_DEFLATED: + finish(); + e.size = def.getBytesRead(); + e.csize = def.getBytesWritten(); + e.crc = crc.getValue(); + break; + case METHOD_STORED: + // we already know that both e.size and e.csize are the same + e.size = e.csize = written; + e.crc = crc.getValue(); + break; + default: + throw new ZipException("invalid compression method"); + } + //crc.reset(); + if (out instanceof ByteArrayOutputStream) + e.bytes = ((ByteArrayOutputStream)out).toByteArray(); + + if (e.type == Entry.FILECH) { + releaseDeflater(def); + return; + } + super.close(); + releaseDeflater(def); + update(e); + } + } + + private static void zerror(String msg) { + throw new ZipError(msg); + } + + // Maxmum number of de/inflater we cache + private final int MAX_FLATER = 20; + // List of available Inflater objects for decompression + private List<Inflater> inflaters = new ArrayList<>(); + + // Gets an inflater from the list of available inflaters or allocates + // a new one. + private Inflater getInflater() { + synchronized (inflaters) { + int size = inflaters.size(); + if (size > 0) { + Inflater inf = (Inflater)inflaters.remove(size - 1); + return inf; + } else { + return new Inflater(true); + } + } + } + + // Releases the specified inflater to the list of available inflaters. + private void releaseInflater(Inflater inf) { + synchronized (inflaters) { + if (inflaters.size() < MAX_FLATER) { + inf.reset(); + inflaters.add(inf); + } else { + inf.end(); + } + } + } + + // List of available Deflater objects for compression + private List<Deflater> deflaters = new ArrayList<>(); + + // Gets an deflater from the list of available deflaters or allocates + // a new one. + private Deflater getDeflater() { + synchronized (deflaters) { + int size = deflaters.size(); + if (size > 0) { + Deflater def = (Deflater)deflaters.remove(size - 1); + return def; + } else { + return new Deflater(Deflater.DEFAULT_COMPRESSION, true); + } + } + } + + // Releases the specified inflater to the list of available inflaters. + private void releaseDeflater(Deflater def) { + synchronized (deflaters) { + if (inflaters.size() < MAX_FLATER) { + def.reset(); + deflaters.add(def); + } else { + def.end(); + } + } + } + + // End of central directory record + static class END { + int disknum; + int sdisknum; + int endsub; // endsub + int centot; // 4 bytes + long cenlen; // 4 bytes + long cenoff; // 4 bytes + int comlen; // comment length + byte[] comment; + + /* members of Zip64 end of central directory locator */ + int diskNum; + long endpos; + int disktot; + + void write(OutputStream os, long offset) throws IOException { + boolean hasZip64 = false; + long xlen = cenlen; + long xoff = cenoff; + if (xlen >= ZIP64_MINVAL) { + xlen = ZIP64_MINVAL; + hasZip64 = true; + } + if (xoff >= ZIP64_MINVAL) { + xoff = ZIP64_MINVAL; + hasZip64 = true; + } + int count = centot; + if (count >= ZIP64_MINVAL32) { + count = ZIP64_MINVAL32; + hasZip64 = true; + } + if (hasZip64) { + long off64 = offset; + //zip64 end of central directory record + writeInt(os, ZIP64_ENDSIG); // zip64 END record signature + writeLong(os, ZIP64_ENDHDR - 12); // size of zip64 end + writeShort(os, 45); // version made by + writeShort(os, 45); // version needed to extract + writeInt(os, 0); // number of this disk + writeInt(os, 0); // central directory start disk + writeLong(os, centot); // number of directory entires on disk + writeLong(os, centot); // number of directory entires + writeLong(os, cenlen); // length of central directory + writeLong(os, cenoff); // offset of central directory + + //zip64 end of central directory locator + writeInt(os, ZIP64_LOCSIG); // zip64 END locator signature + writeInt(os, 0); // zip64 END start disk + writeLong(os, off64); // offset of zip64 END + writeInt(os, 1); // total number of disks (?) + } + writeInt(os, ENDSIG); // END record signature + writeShort(os, 0); // number of this disk + writeShort(os, 0); // central directory start disk + writeShort(os, count); // number of directory entries on disk + writeShort(os, count); // total number of directory entries + writeInt(os, xlen); // length of central directory + writeInt(os, xoff); // offset of central directory + if (comment != null) { // zip file comment + writeShort(os, comment.length); + writeBytes(os, comment); + } else { + writeShort(os, 0); + } + } + } + + // wrapper for the byte[] name + static class EntryName { + byte[] name; + int hashcode; // node is hashable/hashed by its name + + public EntryName (byte[] name) { + name(name); + } + + void name(byte[] name) { + this.name = name; + this.hashcode = Arrays.hashCode(name); + } + + public boolean equals(Object other) { + if (!(other instanceof EntryName)) + return false; + return Arrays.equals(name, ((EntryName)other).name); + } + + public int hashCode() { + return hashcode; + } + } + + // can simply use Integer instead, if we don't use it to + // build a internal node tree. + static class IndexNode { + byte[] name; + int pos = -1; // postion in cen table, -1 menas the + // entry does not exists in zip file + IndexNode(byte[] name, int pos) { + this.name = name; + this.pos = pos; + } + + IndexNode() {} + + IndexNode sibling; + IndexNode child; // 1st child + } + + static class Entry extends IndexNode { + + static final int CEN = 1; // entry read from cen + static final int NEW = 2; // updated contents in bytes or file + static final int FILECH = 3; // fch update in "file" + static final int COPY = 4; // copy of a CEN entry + + byte[] bytes; // updated content bytes + Path file; // use tmp file to store bytes; + int type = CEN; // default is the entry read from cen + + // entry attributes + int version; + int flag; + int method = -1; // compression method + long mtime = -1; // last modification time (in DOS time) + long atime = -1; // last access time + long ctime = -1; // create time + long crc = -1; // crc-32 of entry data + long csize = -1; // compressed size of entry data + long size = -1; // uncompressed size of entry data + int nlen; + int elen; + byte[] extra; + + // loc + long startPos; + long endPos; // exclusive + + // cen + int versionMade; + int disk; + int attrs; + long attrsEx; + long locoff; + + int clen; + byte[] comment; + + // ZIP64 flag + boolean hasZip64; + + Entry() {} + + Entry(byte[] name) { + this.name = name; + //this.nlen = name.length; + this.mtime = javaToDosTime(System.currentTimeMillis()); + this.crc = 0; + this.size = 0; + this.csize = 0; + this.method = METHOD_DEFLATED; + } + + Entry(byte[] name, int type) { + this(name); + this.type = type; + } + + Entry (byte[] name, Path file, int type) { + this(name, type); + this.file = file; + this.method = METHOD_STORED; + } + + Entry(Entry e) { + this.version = e.version; + this.name = e.name; // copyOf? + this.nlen = e.nlen; + this.ctime = e.ctime; + this.atime = e.atime; + this.mtime = e.mtime; + this.crc = e.crc; + this.size = e.size; + this.csize = e.csize; + this.method = e.method; + this.extra = (e.extra == null)? + null:Arrays.copyOf(e.extra, e.extra.length); + this.elen = e.elen; + this.versionMade = e.versionMade; + this.disk = e.disk; + this.attrs = e.attrs; + this.attrsEx = e.attrsEx; + this.locoff = e.locoff; + this.clen = e.clen; + this.comment = (e.comment == null)? + null:Arrays.copyOf(e.comment, e.comment.length); + this.startPos = e.startPos; + this.endPos = e.endPos; + this.hasZip64 = e.hasZip64;; + } + + Entry (Entry e, int type) { + this(e); + this.type = type; + } + + boolean isDir() { + return name != null && + (name.length == 0 || + name[name.length - 1] == '/'); + } + + int version() throws ZipException { + if (method == METHOD_DEFLATED) + return 20; + else if (method == METHOD_STORED) + return 10; + throw new ZipException("unsupported compression method"); + } + + ///////////////////// CEN ////////////////////// + static Entry readCEN(ByteBuffer cen, int pos) throws IOException + { + return new Entry().cen(cen, pos); + } + + private Entry cen(ByteBuffer cen, int pos) throws IOException + { + if (CENSIG(cen, pos) != CENSIG) + zerror("invalid CEN header (bad signature)"); + versionMade = CENVEM(cen, pos); + version = CENVER(cen, pos); + flag = CENFLG(cen, pos); + method = CENHOW(cen, pos); + mtime = CENTIM(cen, pos); + crc = CENCRC(cen, pos); + csize = CENSIZ(cen, pos); + size = CENLEN(cen, pos); + nlen = CENNAM(cen, pos); + elen = CENEXT(cen, pos); + clen = CENCOM(cen, pos); + disk = CENDSK(cen, pos); + attrs = CENATT(cen, pos); + attrsEx = CENATX(cen, pos); + locoff = CENOFF(cen, pos); + + cen.position(pos + CENHDR); + name = new byte[nlen]; + cen.get(name); + + if (elen > 0) { + extra = new byte[elen]; + cen.get(extra); + if (csize == ZIP64_MINVAL || size == ZIP64_MINVAL || + locoff == ZIP64_MINVAL) { + int off = 0; + while (off + 4 < elen) { + // extra spec: HeaderID+DataSize+Data + int sz = SH(extra, off + 2); + if (SH(extra, off) == EXTID_ZIP64) { + off += 4; + if (size == ZIP64_MINVAL) { + // if invalid zip64 extra fields, just skip + if (sz < 8 || (off + 8) > elen) + break; + size = LL(extra, off); + sz -= 8; + off += 8; + } + if (csize == ZIP64_MINVAL) { + if (sz < 8 || (off + 8) > elen) + break; + csize = LL(extra, off); + sz -= 8; + off += 8; + } + if (locoff == ZIP64_MINVAL) { + if (sz < 8 || (off + 8) > elen) + break; + locoff = LL(extra, off); + sz -= 8; + off += 8; + } + break; + } + off += (sz + 4); + } + } + } + if (clen > 0) { + comment = new byte[clen]; + cen.get(comment); + } + return this; + } + + int writeCEN(OutputStream os) throws IOException + { + int written = CENHDR; + int version0 = version(); + + long csize0 = csize; + long size0 = size; + long locoff0 = locoff; + int e64len = 0; + + // confirm size/length + nlen = (name != null) ? name.length : 0; + elen = (extra != null) ? extra.length : 0; + clen = (comment != null) ? comment.length : 0; + + boolean hasZip64 = false; + if (csize >= ZIP64_MINVAL) { + csize0 = ZIP64_MINVAL; + e64len += 8; // csize(8) + hasZip64 = true; + } + if (size >= ZIP64_MINVAL) { + size0 = ZIP64_MINVAL; // size(8) + e64len += 8; + hasZip64 = true; + } + if (locoff >= ZIP64_MINVAL) { + locoff0 = ZIP64_MINVAL; + e64len += 8; // offset(8) + hasZip64 = true; + } + writeInt(os, CENSIG); // CEN header signature + if (hasZip64) { + writeShort(os, 45); // ver 4.5 for zip64 + writeShort(os, 45); + } else { + writeShort(os, version0); // version made by + writeShort(os, version0); // version needed to extract + } + writeShort(os, flag); // general purpose bit flag + writeShort(os, method); // compression method + writeInt(os, mtime); // last modification time + writeInt(os, crc); // crc-32 + writeInt(os, csize0); // compressed size + writeInt(os, size0); // uncompressed size + writeShort(os, name.length); + + if (hasZip64) { + // + headid(2) + datasize(2) + writeShort(os, e64len + 4 + elen); + } else { + writeShort(os, elen); + } + if (comment != null) { + writeShort(os, Math.min(clen, 0xffff)); + } else { + writeShort(os, 0); + } + writeShort(os, 0); // starting disk number + writeShort(os, 0); // internal file attributes (unused) + writeInt(os, 0); // external file attributes (unused) + writeInt(os, locoff0); // relative offset of local header + writeBytes(os, name); + if (hasZip64) { + writeShort(os, EXTID_ZIP64);// Zip64 extra + writeShort(os, e64len); + if (size0 == ZIP64_MINVAL) + writeLong(os, size); + if (csize0 == ZIP64_MINVAL) + writeLong(os, csize); + if (locoff0 == ZIP64_MINVAL) + writeLong(os, locoff); + } + if (extra != null) { + writeBytes(os, extra); + } + if (comment != null) { + //TBD: 0, Math.min(commentBytes.length, 0xffff)); + writeBytes(os, comment); + } + return CENHDR + nlen + elen + clen + (hasZip64?(e64len + 4):0); + } + + ///////////////////// LOC ////////////////////// + static Entry readLOC(ZipFileSystem zf, long pos) + throws IOException + { + return readLOC(zf, pos, new byte[1024]); + } + + static Entry readLOC(ZipFileSystem zf, long pos, byte[] buf) + throws IOException + { + return new Entry().loc(zf, pos, buf); + } + + Entry loc(ZipFileSystem zf, long pos, byte[] buf) + throws IOException + { + assert (buf.length >= LOCHDR); + if (zf.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR) { + throw new ZipException("loc: reading failed"); + } + if (LOCSIG(buf) != LOCSIG) { + throw new ZipException("loc: wrong sig ->" + + Long.toString(LOCSIG(buf), 16)); + } + startPos = pos; + version = LOCVER(buf); + flag = LOCFLG(buf); + method = LOCHOW(buf); + mtime = LOCTIM(buf); + crc = LOCCRC(buf); + csize = LOCSIZ(buf); + size = LOCLEN(buf); + nlen = LOCNAM(buf); + elen = LOCEXT(buf); + + name = new byte[nlen]; + if (zf.readFullyAt(name, 0, nlen, pos + LOCHDR) != nlen) { + throw new ZipException("loc: name reading failed"); + } + if (elen > 0) { + extra = new byte[elen]; + if (zf.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen) + != elen) { + throw new ZipException("loc: ext reading failed"); + } + } + pos += (LOCHDR + nlen + elen); + if ((flag & FLAG_DATADESCR) != 0) { + // Data Descriptor + Entry e = zf.getEntry0(name); // get the size/csize from cen + if (e == null) + throw new ZipException("loc: name not found in cen"); + size = e.size; + csize = e.csize; + pos += (method == METHOD_STORED ? size : csize); + if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL) + pos += 24; + else + pos += 16; + } else { + boolean hasZip64 = false; + if (extra != null && + (size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) { + // zip64 ext: must include both size and csize + int off = 0; + while (off + 20 < elen) { // HeaderID+DataSize+Data + int sz = SH(extra, off + 2); + if (SH(extra, off) == EXTID_ZIP64 && sz == 16) { + size = LL(extra, off + 4); + csize = LL(extra, off + 12); + hasZip64 = true; + break; + } + off += (sz + 4); + } + } + pos += (method == METHOD_STORED ? size : csize); + } + endPos = pos; + return this; + } + + int writeLOC(OutputStream os) + throws IOException + { + writeInt(os, LOCSIG); // LOC header signature + + int version = version(); + if ((flag & FLAG_DATADESCR) != 0) { + writeShort(os, version()); // version needed to extract + writeShort(os, flag); // general purpose bit flag + writeShort(os, method); // compression method + writeInt(os, mtime); // last modification time + + // store size, uncompressed size, and crc-32 in data descriptor + // immediately following compressed entry data + writeInt(os, 0); + writeInt(os, 0); + writeInt(os, 0); + } else { + if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { + hasZip64 = true; + writeShort(os, 45); // ver 4.5 for zip64 + } else { + writeShort(os, version()); // version needed to extract + } + writeShort(os, flag); // general purpose bit flag + writeShort(os, method); // compression method + writeInt(os, mtime); // last modification time + writeInt(os, crc); // crc-32 + if (hasZip64) { + writeInt(os, ZIP64_MINVAL); + writeInt(os, ZIP64_MINVAL); + //TBD: e.elen += 20; //headid(2) + size(2) + size(8) + csize(8) + } else { + writeInt(os, csize); // compressed size + writeInt(os, size); // uncompressed size + } + } + writeShort(os, name.length); + writeShort(os, elen + (hasZip64 ? 20 : 0)); + writeBytes(os, name); + if (hasZip64) { + // TBD: should we update extra directory? + writeShort(os, EXTID_ZIP64); + writeShort(os, 16); + writeLong(os, size); + writeLong(os, csize); + } + if (extra != null) { + writeBytes(os, extra); + } + return LOCHDR + name.length + elen + (hasZip64 ? 20 : 0); + } + + // Data Descriptior + int writeEXT(OutputStream os) + throws IOException + { + writeInt(os, EXTSIG); // EXT header signature + writeInt(os, crc); // crc-32 + if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { + writeLong(os, csize); + writeLong(os, size); + return 24; + } else { + writeInt(os, csize); // compressed size + writeInt(os, size); // uncompressed size + return 16; + } + } + + // read NTFS, UNIX and ZIP64 data from cen.extra + void readExtra() { + if (extra == null) + return; + int elen = extra.length; + int off = 0; + while (off + 4 < elen) { + // extra spec: HeaderID+DataSize+Data + int sz = SH(extra, off + 2); + int tag = SH(extra, off); + off += 4; + int pos = off; + if (pos + sz > elen) // invalid data + break; + switch (tag) { + case EXTID_ZIP64 : + if (size == ZIP64_MINVAL) { + if (pos + 8 > elen) // invalid zip64 extra + break; // fields, just skip + size = LL(extra, pos); + pos += 8; + } + if (csize == ZIP64_MINVAL) { + if (pos + 8 > elen) + break; + csize = LL(extra, pos); + pos += 8; + } + if (locoff == ZIP64_MINVAL) { + if (pos + 8 > elen) + break; + locoff = LL(extra, pos); + pos += 8; + } + break; + case EXTID_NTFS: + pos += 4; // reserved 4 bytes + if (SH(extra, pos) != 0x0001) + break; + if (SH(extra, pos + 2) != 24) + break; + mtime = LL(extra, pos + 4); + atime = LL(extra, pos + 12); + ctime = LL(extra, pos + 20); + break; + case EXTID_UNIX: + atime = LG(extra, pos); + mtime = LG(extra, pos + 4); + break; + default: // unknow + } + off += sz; + } + } + } + + private static class ExChannelCloser { + Path path; + SeekableByteChannel ch; + Set<InputStream> streams; + ExChannelCloser(Path path, + SeekableByteChannel ch, + Set<InputStream> streams) + { + this.path = path; + this.ch = ch; + this.streams = streams; + } + } + + // ZIP directory has two issues: + // (1) ZIP spec does not require the ZIP file to include + // directory entry + // (2) all entries are not stored/organized in a "tree" + // structure. + // A possible solution is to build the node tree ourself as + // implemented below. + private HashMap<EntryName, IndexNode> dirs; + private IndexNode root; + private IndexNode addToDir(EntryName child) { + IndexNode cinode = dirs.get(child); + if (cinode != null) + return cinode; + + byte[] cname = child.name; + byte[] pname = getParent(cname); + IndexNode pinode; + + if (pname != null) + pinode = addToDir(new EntryName(pname)); + else + pinode = root; + cinode = inodes.get(child); + if (cname[cname.length -1] != '/') { // not a dir + cinode.sibling = pinode.child; + pinode.child = cinode; + return null; + } + cinode = dirs.get(child); + if (cinode == null) // pseudo directry entry + cinode = new IndexNode(cname, -1); + cinode.sibling = pinode.child; + pinode.child = cinode; + + dirs.put(child, cinode); + return cinode; + } + + private HashMap<EntryName, IndexNode> getDirs() + throws IOException + { + if (hasUpdate) + sync(); + if (dirs != null) + return dirs; + dirs = new HashMap<EntryName, IndexNode>(); + byte[] empty = new byte[0]; + root = new IndexNode(empty, -1); + dirs.put(new EntryName(empty), root); + + EntryName[] names = inodes.keySet().toArray(new EntryName[0]); + int i = names.length; + while (--i >= 0) { + addToDir(names[i]); + } + // for (int i EntryName en : inodes.keySet()) { + // addToDir(en); + // } + return dirs; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.FileRef; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystemAlreadyExistsException; +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.FileAttribute; +import java.nio.file.spi.FileSystemProvider; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/* + * + * @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 "zip"; + } + + protected Path uriToPath(URI uri) { + String scheme = uri.getScheme(); + if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { + throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); + } + try { + return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null)) + .toAbsolutePath(); + } catch (URISyntaxException e) { + throw new AssertionError(e); //never thrown + } + } + + @Override + public FileSystem newFileSystem(URI uri, Map<String, ?> env) + throws IOException + { + return newFileSystem(uriToPath(uri), env); + } + + @Override + public FileSystem newFileSystem(FileRef file, Map<String, ?> env) + throws IOException + { + if (!(file instanceof Path)) + throw new UnsupportedOperationException(); + Path path = (Path)file; + if (!path.toUri().getScheme().equalsIgnoreCase("file")) { + throw new UnsupportedOperationException(); + } + return newFileSystem(path, env); + } + + private FileSystem newFileSystem(Path path, Map<String, ?> env) + throws IOException + { + synchronized(filesystems) { + if (filesystems.containsKey(path)) + throw new FileSystemAlreadyExistsException(); + ZipFileSystem zipfs = new ZipFileSystem(this, path, env); + filesystems.put(path, zipfs); + return zipfs; + } + } + + @Override + public Path getPath(URI uri) { + FileSystem fs = getFileSystem(uri); + String fragment = uri.getFragment(); + if (fragment == null) { + throw new IllegalArgumentException("URI: " + + uri + + " does not contain path fragment ex. zip:///c:/foo.zip#/BAR"); + } + return fs.getPath(fragment); + } + + @Override + public FileChannel newFileChannel(Path path, + Set<? extends OpenOption> options, + FileAttribute<?>... attrs) + throws IOException + { + if (path == null) + throw new NullPointerException("path is null"); + if (path instanceof ZipPath) + return ((ZipPath)path).newFileChannel(options, attrs); + throw new ProviderMismatchException(); + } + + @Override + public FileSystem getFileSystem(URI uri) { + synchronized (filesystems) { + ZipFileSystem zipfs = filesystems.get(uriToPath(uri)); + if (zipfs == null) + throw new FileSystemNotFoundException(); + return zipfs; + } + } + + void removeFileSystem(Path zfpath) { + synchronized (filesystems) { + filesystems.remove(zfpath); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.PrintStream; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import com.sun.nio.zipfs.ZipFileSystem.Entry; +import static com.sun.nio.zipfs.ZipConstants.*; +import static com.sun.nio.zipfs.ZipUtils.*; + +/** + * Print the loc and cen tables of the ZIP file + * + * @author Xueming Shen + */ + +public class ZipInfo { + + public static void main(String[] args) throws Throwable { + if (args.length < 2) { + print("Usage: java ZipInfo [cen|loc] zfname"); + } else { + Map<String, ?> env = Collections.emptyMap(); + ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider() + .newFileSystem(Paths.get(args[1]), env)); + + long pos = 0; + + if ("loc".equals(args[0])) { + print("[Local File Header]%n"); + byte[] buf = new byte[1024]; + for (int i = 0; i < zfs.getEntryNames().length; i++) { + Entry loc = Entry.readLOC(zfs, pos, buf); + print("--------loc[%x]--------%n", pos); + printLOC(loc); + pos = loc.endPos; + } + } if ("cen".equals(args[0])) { + int i = 0; + Iterator<ZipFileSystem.IndexNode> itr = zfs.inodes.values().iterator(); + print("[Central Directory Header]%n"); + while (itr.hasNext()) { + Entry cen = Entry.readCEN(zfs.cen, itr.next().pos); + print("--------cen[%d]--------%n", i); + printCEN(cen); + i++; + } + } + zfs.close(); + } + } + + static void print(String fmt, Object... objs) { + System.out.printf(fmt, objs); + } + + static void printLOC(Entry loc) { + print(" [%x, %x]%n", loc.startPos, loc.endPos); + print(" Signature : %8x%n", LOCSIG); + print(" Version : %4x [%d.%d]%n", + loc.version, loc. version/10, loc. version%10); + print(" Flag : %4x%n", loc.flag); + print(" Method : %4x%n", loc. method); + print(" LastMTime : %8x [%tc]%n", + loc.mtime, dosToJavaTime(loc.mtime)); + print(" CRC : %8x%n", loc.crc); + print(" CSize : %8x%n", loc.csize); + print(" Size : %8x%n", loc.size); + print(" NameLength : %4x [%s]%n", + loc.nlen, new String(loc.name)); + print(" ExtraLength : %4x%n", loc.elen); + if (loc.hasZip64) + print(" *ZIP64*%n"); + } + + static void printCEN(Entry cen) { + print(" Signature : %08x%n", CENSIG); + print(" VerMadeby : %4x [%d.%d]%n", + cen.versionMade, cen.versionMade/10, cen.versionMade%10); + print(" VerExtract : %4x [%d.%d]%n", + cen.version, cen.version/10, cen.version%10); + print(" Flag : %4x%n", cen.flag); + print(" Method : %4x%n", cen.method); + print(" LastMTime : %8x [%tc]%n", + cen.mtime, dosToJavaTime(cen.mtime)); + print(" CRC : %8x%n", cen.crc); + print(" CSize : %8x%n", cen.csize); + print(" Size : %8x%n", cen.size); + print(" NameLen : %4x [%s]%n", + cen.nlen, new String(cen.name)); + print(" ExtraLen : %4x%n", cen.elen); + print(" CommentLen : %4x%n", cen.clen); + print(" DiskStart : %4x%n", cen.disk); + print(" Attrs : %4x%n", cen.attrs); + print(" AttrsEx : %8x%n", cen.attrsEx); + print(" LocOff : %8x%n", cen.locoff); + if (cen.hasZip64) + print(" *ZIP64*%n"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.File; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.*; +import java.nio.file.DirectoryStream.Filter; +import java.nio.file.spi.FileSystemProvider; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + +/** + * + * @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal + */ + +public class ZipPath extends 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 getName() { + 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(boolean resolveLinks) throws IOException { + ZipPath realPath = new ZipPath(zfs, getResolvedPath()); + realPath.checkAccess(); + return realPath; + } + + @Override + public 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() { + String zfPath = zfs.toString(); + if (File.separatorChar == '\\') // replace all separators by '/' + zfPath = "/" + zfPath.replace("\\", "/"); + try { + return new URI("zip", "", + zfPath, + zfs.getString(toAbsolutePath().path)); + } 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 null; + 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[0] == '/'); + } + + @Override + public ZipPath resolve(Path other) { + if (other == null) + return this; + 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 ZipPath resolve(String other) { + return resolve(getFileSystem().getPath(other)); + } + + @Override + public boolean startsWith(Path other) { + final ZipPath o = checkPath(other); + if (o.isAbsolute() != this.isAbsolute()) + return false; + final int oCount = o.getNameCount(); + if (getNameCount() < oCount) + return false; + for (int i = 0; i < oCount; i++) { + if (!o.getName(i).equals(getName(i))) + return false; + } + return true; + } + + @Override + public boolean endsWith(Path other) { + final ZipPath o = checkPath(other); + if (o.isAbsolute()) + return this.isAbsolute() ? this.equals(o) : false; + int i = o.getNameCount(); + int j = this.getNameCount(); + if (j < i) + return false; + for (--i, --j; i >= 0; i--, j--) { + if (!o.getName(i).equals(this.getName(j))) + return false; + } + return true; + } + + @Override + public Path normalize() { + byte[] resolved = getResolved(); + if (resolved == path) // no change + return this; + if (resolved.length == 0) + return null; + 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)'.') + 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; + } + + @Override + public Path createSymbolicLink( + Path target, FileAttribute<?>... attrs) throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Path createLink( + Path existing) throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Path readSymbolicLink() throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Path createDirectory(FileAttribute<?>... attrs) + throws IOException + { + zfs.createDirectory(getResolvedPath(), attrs); + return this; + } + + public final Path createFile(FileAttribute<?>... attrs) + throws IOException + { + OutputStream os = newOutputStream(CREATE_NEW, WRITE); + try { + os.close(); + } catch (IOException x) {} + return this; + } + + @Override + public 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()); + } + + private static final DirectoryStream.Filter<Path> acceptAllFilter = + new DirectoryStream.Filter<Path>() { + @Override public boolean accept(Path entry) { return true; } + }; + + @Override + public final DirectoryStream<Path> newDirectoryStream() throws IOException { + return newDirectoryStream(acceptAllFilter); + } + + @Override + public DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter) + throws IOException + { + return new ZipDirectoryStream(this, filter); + } + + @Override + public final DirectoryStream<Path> newDirectoryStream(String glob) + throws IOException + { + // avoid creating a matcher if all entries are required. + if (glob.equals("*")) + return newDirectoryStream(); + + // create a matcher and return a filter that uses it. + final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob); + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { + @Override + public boolean accept(Path entry) { + return matcher.matches(entry.getName()); + } + }; + return newDirectoryStream(filter); + } + + @Override + public final void delete() throws IOException { + zfs.deleteFile(getResolvedPath(), true); + } + + @Override + public final 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; + } + + @Override + @SuppressWarnings("unchecked") + public <V extends FileAttributeView> V getFileAttributeView(Class<V> type, + LinkOption... options) + { + return (V)ZipFileAttributeView.get(this, type); + } + + @Override + public 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); + } + + private Object getAttributesImpl(String attribute, boolean domap) + throws IOException + { + String view = null; + String attr = null; + int colonPos = attribute.indexOf(':'); + if (colonPos == -1) { + view = "basic"; + attr = attribute; + } else { + view = attribute.substring(0, colonPos++); + attr = attribute.substring(colonPos); + } + ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view); + if (zfv == null) { + throw new UnsupportedOperationException("view not supported"); + } + return zfv.getAttribute(attr, domap); + } + + @Override + public Object getAttribute(String attribute, LinkOption... options) + throws IOException + { + return getAttributesImpl(attribute, false); + } + + @Override + public Map<String,?> readAttributes(String attribute, LinkOption... options) + throws IOException + { + return (Map<String, ?>)getAttributesImpl(attribute, true); + } + + @Override + public 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)); + } + + @Override + public boolean isSameFile(Path other) throws IOException { + if (other == null || + this.getFileSystem() != other.getFileSystem()) + return false; + this.checkAccess(); + other.checkAccess(); + return Arrays.equals(this.getResolvedPath(), + ((ZipPath)other).getResolvedPath()); + } + + 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 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(); + } + }; + } + + @Override + public 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); + } + + @Override + public SeekableByteChannel newByteChannel(OpenOption... options) + throws IOException { + Set<OpenOption> set = new HashSet<OpenOption>(options.length); + Collections.addAll(set, options); + return newByteChannel(set); + } + + @Override + public 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()); + + } + + @Override + public boolean exists() { + if (path.length == 1 && path[0] == '/') + return true; + try { + return zfs.exists(getResolvedPath()); + } catch (IOException x) {} + return false; + } + + @Override + public boolean notExists() { + return !exists(); + } + + + @Override + public OutputStream newOutputStream(OpenOption... options) + throws IOException + { + if (options.length == 0) + return zfs.newOutputStream(getResolvedPath(), + CREATE_NEW, WRITE); + return zfs.newOutputStream(getResolvedPath(), options); + } + + @Override + public Path moveTo(Path target, CopyOption... options) + throws IOException + { + if (this.zfs.provider() == target.getFileSystem().provider() && + this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile())) + { + zfs.copyFile(true, + getResolvedPath(), + ((ZipPath)target).getResolvedPath(), + options); + } else { + copyToTarget(target, options); + delete(); + } + return target; + } + + @Override + public Path copyTo(Path target, CopyOption... options) + throws IOException + { + if (this.zfs.provider() == target.getFileSystem().provider() && + this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile())) + { + zfs.copyFile(false, + getResolvedPath(), + ((ZipPath)target).getResolvedPath(), + options); + } else { + copyToTarget(target, options); + } + return target; + } + + private void copyToTarget(Path 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 = false; + } + // 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 = + target.getFileAttributeView(BasicFileAttributeView.class); + try { + view.setTimes(zfas.lastModifiedTime(), null, null); + } catch (IOException x) { + // rollback? + try { + target.delete(); + } catch (IOException ignore) { } + throw x; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2009, 2010, 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. + */ + +package com.sun.nio.zipfs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Date; +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 >>> 0) & 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 >>> 0) & 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 >>> 0) & 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; + } + + 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(); + } +}
--- a/jdk/src/solaris/classes/sun/awt/X11/GtkFileDialogPeer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/GtkFileDialogPeer.java Wed Oct 13 17:51:41 2010 -0700 @@ -64,7 +64,10 @@ accessor.setFile(fd, null); accessor.setFiles(fd, null, null); } else { - accessor.setDirectory(fd, directory); + // Fix 6987233: add the trailing slash if it's absent + accessor.setDirectory(fd, directory + + (directory.endsWith(File.separator) ? + "" : File.separator)); accessor.setFile(fd, filenames[0]); accessor.setFiles(fd, directory, filenames); }
--- a/jdk/src/solaris/classes/sun/awt/X11/XBaseWindow.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/XBaseWindow.java Wed Oct 13 17:51:41 2010 -0700 @@ -705,12 +705,8 @@ throw new IllegalStateException("Attempt to resize uncreated window"); } insLog.fine("Setting bounds on " + this + " to (" + x + ", " + y + "), " + width + "x" + height); - if (width <= 0) { - width = 1; - } - if (height <= 0) { - height = 1; - } + width = Math.max(MIN_SIZE, width); + height = Math.max(MIN_SIZE, height); XToolkit.awtLock(); try { XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getWindow(), x,y,width,height);
--- a/jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/XDecoratedPeer.java Wed Oct 13 17:51:41 2010 -0700 @@ -763,12 +763,8 @@ } private void checkShellRectSize(Rectangle shellRect) { - if (shellRect.width < 0) { - shellRect.width = 1; - } - if (shellRect.height < 0) { - shellRect.height = 1; - } + shellRect.width = Math.max(MIN_SIZE, shellRect.width); + shellRect.height = Math.max(MIN_SIZE, shellRect.height); } private void checkShellRectPos(Rectangle shellRect) {
--- a/jdk/src/solaris/classes/sun/awt/X11/XEmbeddedFrame.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/XEmbeddedFrame.java Wed Oct 13 17:51:41 2010 -0700 @@ -28,9 +28,12 @@ import sun.awt.EmbeddedFrame; import java.awt.*; import java.awt.AWTKeyStroke; +import java.util.logging.Logger; public class XEmbeddedFrame extends EmbeddedFrame { + private static final Logger log = Logger.getLogger(XEmbeddedFrame.class.getName()); + long handle; public XEmbeddedFrame() { } @@ -70,6 +73,21 @@ this(handle, supportsXEmbed, false); } + /* + * The method shouldn't be called in case of active XEmbed. + */ + public boolean traverseIn(boolean direction) { + XEmbeddedFramePeer peer = (XEmbeddedFramePeer)getPeer(); + if (peer != null) { + if (peer.supportsXEmbed() && peer.isXEmbedActive()) { + log.fine("The method shouldn't be called when XEmbed is active!"); + } else { + return super.traverseIn(direction); + } + } + return false; + } + protected boolean traverseOut(boolean direction) { XEmbeddedFramePeer xefp = (XEmbeddedFramePeer) getPeer(); if (direction == FORWARD) { @@ -81,6 +99,20 @@ return true; } + /* + * The method shouldn't be called in case of active XEmbed. + */ + public void synthesizeWindowActivation(boolean doActivate) { + XEmbeddedFramePeer peer = (XEmbeddedFramePeer)getPeer(); + if (peer != null) { + if (peer.supportsXEmbed() && peer.isXEmbedActive()) { + log.fine("The method shouldn't be called when XEmbed is active!"); + } else { + peer.synthesizeFocusInOut(doActivate); + } + } + } + public void registerAccelerator(AWTKeyStroke stroke) { XEmbeddedFramePeer xefp = (XEmbeddedFramePeer) getPeer(); if (xefp != null) {
--- a/jdk/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/XEmbeddedFramePeer.java Wed Oct 13 17:51:41 2010 -0700 @@ -35,6 +35,8 @@ import sun.awt.EmbeddedFrame; import sun.awt.SunToolkit; +import static sun.awt.X11.XConstants.*; + public class XEmbeddedFramePeer extends XFramePeer { private static final PlatformLogger xembedLog = PlatformLogger.getLogger("sun.awt.X11.xembed.XEmbeddedFramePeer"); @@ -305,4 +307,20 @@ EmbeddedFrame frame = (EmbeddedFrame)target; frame.notifyModalBlocked(blocker, blocked); } + + public void synthesizeFocusInOut(boolean doFocus) { + XFocusChangeEvent xev = new XFocusChangeEvent(); + + XToolkit.awtLock(); + try { + xev.set_type(doFocus ? FocusIn : FocusOut); + xev.set_window(getFocusProxy().getWindow()); + xev.set_mode(NotifyNormal); + XlibWrapper.XSendEvent(XToolkit.getDisplay(), getFocusProxy().getWindow(), false, + NoEventMask, xev.pData); + } finally { + XToolkit.awtUnlock(); + xev.dispose(); + } + } }
--- a/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/classes/sun/awt/X11/XToolkit.java Wed Oct 13 17:51:41 2010 -0700 @@ -1482,8 +1482,19 @@ try { if (numberOfButtons == 0) { numberOfButtons = getNumberOfButtonsImpl(); + numberOfButtons = (numberOfButtons > MAX_BUTTONS_SUPPORTED)? MAX_BUTTONS_SUPPORTED : numberOfButtons; + //4th and 5th buttons are for wheel and shouldn't be reported as buttons. + //If we have more than 3 physical buttons and a wheel, we report N-2 buttons. + //If we have 3 physical buttons and a wheel, we report 3 buttons. + //If we have 1,2,3 physical buttons, we report it as is i.e. 1,2 or 3 respectively. + if (numberOfButtons >=5) { + numberOfButtons -= 2; + } else if (numberOfButtons == 4 || numberOfButtons ==5){ + numberOfButtons = 3; + } } - return (numberOfButtons > MAX_BUTTONS_SUPPORTED)? MAX_BUTTONS_SUPPORTED : numberOfButtons; + //Assume don't have to re-query the number again and again. + return numberOfButtons; } finally { awtUnlock(); }
--- a/jdk/src/solaris/native/sun/awt/awt_InputMethod.c Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/solaris/native/sun/awt/awt_InputMethod.c Wed Oct 13 17:51:41 2010 -0700 @@ -1473,6 +1473,10 @@ static void DestroyXIMCallback(XIM im, XPointer client_data, XPointer call_data) { /* mark that XIM server was destroyed */ X11im = NULL; + JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); + /* free the old pX11IMData and set it to null. this also avoids crashing + * the jvm if the XIM server reappears */ + X11InputMethodData *pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance); } /*
--- a/jdk/src/windows/classes/sun/awt/windows/WComponentPeer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/classes/sun/awt/windows/WComponentPeer.java Wed Oct 13 17:51:41 2010 -0700 @@ -556,24 +556,26 @@ Component target = (Component)getTarget(); Window window = SunToolkit.getContainingWindow(target); - if (window != null && !window.isOpaque()) { - // Non-opaque windows do not support heavyweight children. - // Redirect all painting to the Window's Graphics instead. - // The caller is responsible for calling the - // WindowPeer.updateWindow() after painting has finished. - int x = 0, y = 0; - for (Component c = target; c != window; c = c.getParent()) { - x += c.getX(); - y += c.getY(); - } - + if (window != null) { Graphics g = ((WWindowPeer)window.getPeer()).getTranslucentGraphics(); + // getTranslucentGraphics() returns non-null value for non-opaque windows only + if (g != null) { + // Non-opaque windows do not support heavyweight children. + // Redirect all painting to the Window's Graphics instead. + // The caller is responsible for calling the + // WindowPeer.updateWindow() after painting has finished. + int x = 0, y = 0; + for (Component c = target; c != window; c = c.getParent()) { + x += c.getX(); + y += c.getY(); + } - g.translate(x, y); - g.clipRect(0, 0, target.getWidth(), target.getHeight()); + g.translate(x, y); + g.clipRect(0, 0, target.getWidth(), target.getHeight()); - return g; + return g; + } } SurfaceData surfaceData = this.surfaceData;
--- a/jdk/src/windows/classes/sun/awt/windows/WEmbeddedFrame.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/classes/sun/awt/windows/WEmbeddedFrame.java Wed Oct 13 17:51:41 2010 -0700 @@ -191,9 +191,20 @@ public void activateEmbeddingTopLevel() { } - public void synthesizeWindowActivation(boolean doActivate) { - ((WEmbeddedFramePeer)getPeer()).synthesizeWmActivate(doActivate); + public void synthesizeWindowActivation(final boolean doActivate) { + if (!doActivate || EventQueue.isDispatchThread()) { + ((WEmbeddedFramePeer)getPeer()).synthesizeWmActivate(doActivate); + } else { + // To avoid focus concurrence b/w IE and EmbeddedFrame + // activation is postponed by means of posting it to EDT. + EventQueue.invokeLater(new Runnable() { + public void run() { + ((WEmbeddedFramePeer)getPeer()).synthesizeWmActivate(true); + } + }); + } } + public void registerAccelerator(AWTKeyStroke stroke) {} public void unregisterAccelerator(AWTKeyStroke stroke) {}
--- a/jdk/src/windows/classes/sun/awt/windows/WWindowPeer.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/classes/sun/awt/windows/WWindowPeer.java Wed Oct 13 17:51:41 2010 -0700 @@ -595,16 +595,6 @@ } @Override - public Graphics getGraphics() { - synchronized (getStateLock()) { - if (!isOpaque) { - return getTranslucentGraphics(); - } - } - return super.getGraphics(); - } - - @Override public void setBackground(Color c) { super.setBackground(c); synchronized (getStateLock()) {
--- a/jdk/src/windows/lib/tzmappings Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/lib/tzmappings Wed Oct 13 17:51:41 2010 -0700 @@ -1,5 +1,4 @@ # -# # This file describes mapping information between Windows and Java # time zones. # Format: Each line should include a colon separated fields of Windows @@ -11,7 +10,7 @@ # NOTE # This table format is not a public interface of any Java # platforms. No applications should depend on this file in any form. -# +# # This table has been generated by a program and should not be edited # manually. # @@ -84,8 +83,8 @@ Ekaterinburg Standard Time:10,11::Asia/Yekaterinburg: West Asia:10,11:UZ:Asia/Tashkent: West Asia Standard Time:10,11:UZ:Asia/Tashkent: -Central Asia:12,13::Asia/Dhaka: -Central Asia Standard Time:12,13::Asia/Dhaka: +Central Asia:12,13::Asia/Almaty: +Central Asia Standard Time:12,13::Asia/Almaty: N. Central Asia Standard Time:12,13::Asia/Novosibirsk: Bangkok:14,15::Asia/Bangkok: Bangkok Standard Time:14,15::Asia/Bangkok: @@ -167,22 +166,27 @@ Greenwich Standard Time:88,89::GMT: Argentina Standard Time:900,900::America/Buenos_Aires: Azerbaijan Standard Time:901,901:AZ:Asia/Baku: -Central Brazilian Standard Time:902,902:BR:America/Manaus: -Central Standard Time (Mexico):903,903::America/Mexico_City: -Georgian Standard Time:904,904:GE:Asia/Tbilisi: -Jordan Standard Time:905,905:JO:Asia/Amman: -Mauritius Standard Time:906,906:MU:Indian/Mauritius: -Middle East Standard Time:907,907:LB:Asia/Beirut: -Montevideo Standard Time:908,908:UY:America/Montevideo: -Morocco Standard Time:909,909:MA:Africa/Casablanca: -Mountain Standard Time (Mexico):910,910:MX:America/Chihuahua: -Namibia Standard Time:911,911:NA:Africa/Windhoek: -Pacific Standard Time (Mexico):912,912:MX:America/Tijuana: -Pakistan Standard Time:913,913::Asia/Karachi: -UTC:914,914::UTC: -Venezuela Standard Time:915,915::America/Caracas: -Kamchatka Standard Time:916,916:RU:Asia/Kamchatka: -Paraguay Standard Time:917,917:PY:America/Asuncion: -Western Brazilian Standard Time:918,918:BR:America/Rio_Branco: -Ulaanbaatar Standard Time:919,919::Asia/Ulaanbaatar: -Armenian Standard Time:920,920:AM:Asia/Yerevan: +Bangladesh Standard Time:902,902::Asia/Dhaka: +Central Brazilian Standard Time:903,903:BR:America/Manaus: +Central Standard Time (Mexico):904,904::America/Mexico_City: +Georgian Standard Time:905,905:GE:Asia/Tbilisi: +Jordan Standard Time:906,906:JO:Asia/Amman: +Kamchatka Standard Time:907,907:RU:Asia/Kamchatka: +Mauritius Standard Time:908,908:MU:Indian/Mauritius: +Middle East Standard Time:909,909:LB:Asia/Beirut: +Montevideo Standard Time:910,910:UY:America/Montevideo: +Morocco Standard Time:911,911:MA:Africa/Casablanca: +Mountain Standard Time (Mexico):912,912:MX:America/Chihuahua: +Namibia Standard Time:913,913:NA:Africa/Windhoek: +Pacific Standard Time (Mexico):914,914:MX:America/Tijuana: +Pakistan Standard Time:915,915::Asia/Karachi: +Paraguay Standard Time:916,916:PY:America/Asuncion: +Syria Standard Time:917,917:SY:Asia/Damascus: +UTC:918,918::UTC: +UTC+12:919,919::GMT+1200: +UTC-02:920,920::GMT-0200: +UTC-11:921,921::GMT-1100: +Ulaanbaatar Standard Time:922,922::Asia/Ulaanbaatar: +Venezuela Standard Time:923,923::America/Caracas: +Western Brazilian Standard Time:924,924:BR:America/Rio_Branco: +Armenian Standard Time:925,925:AM:Asia/Yerevan:
--- a/jdk/src/windows/native/sun/windows/awt_Component.cpp Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp Wed Oct 13 17:51:41 2010 -0700 @@ -2329,6 +2329,19 @@ MSG msg; InitMessage(&msg, lastMessage, flags, MAKELPARAM(x, y), x, y); + AwtWindow *toplevel = GetContainer(); + if (toplevel && !toplevel->IsSimpleWindow()) { + /* + * The frame should be focused by click in case it is + * the active window but not the focused window. See 6886678. + */ + if (toplevel->GetHWnd() == ::GetActiveWindow() && + toplevel->GetHWnd() != AwtComponent::GetFocusedWindow()) + { + toplevel->AwtSetActiveWindow(); + } + } + SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y, GetJavaModifiers(), clickCount, JNI_FALSE, GetButton(button), &msg);
--- a/jdk/src/windows/native/sun/windows/awt_Desktop.cpp Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/native/sun/windows/awt_Desktop.cpp Wed Oct 13 17:51:41 2010 -0700 @@ -59,15 +59,17 @@ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, - GetLastError(), + (int)retval, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR)&buffer, 0, NULL ); - jstring errmsg = JNU_NewStringPlatform(env, buffer, len); - LocalFree(buffer); - return errmsg; + if (buffer) { + jstring errmsg = JNU_NewStringPlatform(env, buffer); + LocalFree(buffer); + return errmsg; + } } return NULL;
--- a/jdk/src/windows/native/sun/windows/awt_DesktopProperties.cpp Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/native/sun/windows/awt_DesktopProperties.cpp Wed Oct 13 17:51:41 2010 -0700 @@ -505,7 +505,8 @@ SetIntegerProperty(TEXT("win.drag.width"), cxdrag); SetIntegerProperty(TEXT("win.drag.height"), cydrag); SetIntegerProperty(TEXT("DnD.gestureMotionThreshold"), max(cxdrag, cydrag)/2); - SetIntegerProperty(TEXT("awt.mouse.numButtons"), GetSystemMetrics(SM_CMOUSEBUTTONS)); + SetIntegerProperty(TEXT("awt.mouse.numButtons"), AwtToolkit::GetNumberOfButtons()); + SetIntegerProperty(TEXT("awt.multiClickInterval"), GetDoubleClickTime()); // BEGIN cross-platform properties
--- a/jdk/src/windows/native/sun/windows/awt_Toolkit.cpp Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/native/sun/windows/awt_Toolkit.cpp Wed Oct 13 17:51:41 2010 -0700 @@ -133,6 +133,8 @@ static LPCTSTR szAwtToolkitClassName = TEXT("SunAwtToolkit"); +static const int MOUSE_BUTTONS_WINDOWS_SUPPORTED = 5; //three standard buttons + XBUTTON1 + XBUTTON2. + UINT AwtToolkit::GetMouseKeyState() { static BOOL mbSwapped = ::GetSystemMetrics(SM_SWAPBUTTON); @@ -2310,5 +2312,9 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_WToolkit_getNumberOfButtonsImpl (JNIEnv *, jobject self) { - return GetSystemMetrics(SM_CMOUSEBUTTONS); + return AwtToolkit::GetNumberOfButtons(); } + +UINT AwtToolkit::GetNumberOfButtons() { + return MOUSE_BUTTONS_WINDOWS_SUPPORTED; +}
--- a/jdk/src/windows/native/sun/windows/awt_Toolkit.h Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/src/windows/native/sun/windows/awt_Toolkit.h Wed Oct 13 17:51:41 2010 -0700 @@ -185,6 +185,7 @@ BOOL IsDynamicLayoutActive(); BOOL areExtraMouseButtonsEnabled(); void setExtraMouseButtonsEnabled(BOOL enable); + static UINT GetNumberOfButtons(); INLINE BOOL localPump() { return m_localPump; } INLINE BOOL VerifyComponents() { return FALSE; } // TODO: Use new DebugHelper class to set this flag
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/demo/zipfs/Basic.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.FileSystemProvider; +import java.util.*; +import java.net.URI; +import java.io.IOException; + +/** + * Basic test for zip provider + */ + +public class Basic { + public static void main(String[] args) throws Exception { + Path zipfile = Paths.get(args[0]); + + // Test: zip should should be returned in provider list + boolean found = false; + + for (FileSystemProvider provider: FileSystemProvider.installedProviders()) { + if (provider.getScheme().equalsIgnoreCase("zip")) { + found = true; + break; + } + } + if (!found) + throw new RuntimeException("'zip' provider not installed"); + + // Test: FileSystems#newFileSystem(FileRef) + Map<String,?> env = new HashMap<String,Object>(); + FileSystems.newFileSystem(zipfile, env, null).close(); + + // Test: FileSystems#newFileSystem(URI) + URI uri = URI.create("zip" + zipfile.toUri().toString().substring(4)); + FileSystem fs = FileSystems.newFileSystem(uri, env, null); + + // Test: exercise toUri method + String expected = uri.toString() + "#/foo"; + String actual = fs.getPath("/foo").toUri().toString(); + if (!actual.equals(expected)) { + throw new RuntimeException("toUri returned '" + actual + + "', expected '" + expected + "'"); + } + + // Test: exercise directory iterator and retrieval of basic attributes + Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); + + // Test: DirectoryStream + found = false; + DirectoryStream<Path> stream = fs.getPath("/").newDirectoryStream(); + try { + for (Path entry: stream) { + found = entry.toString().equals("/META-INF/"); + if (found) break; + } + } finally { + stream.close(); + } + + if (!found) + throw new RuntimeException("Expected file not found"); + + // Test: copy file from zip file to current (scratch) directory + Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); + if (source.exists()) { + Path target = Paths.get(source.getName().toString()); + source.copyTo(target, StandardCopyOption.REPLACE_EXISTING); + try { + long s1 = Attributes.readBasicFileAttributes(source).size(); + long s2 = Attributes.readBasicFileAttributes(target).size(); + if (s2 != s1) + throw new RuntimeException("target size != source size"); + } finally { + target.delete(); + } + } + + // Test: FileStore + FileStore store = fs.getPath("/").getFileStore(); + if (!store.supportsFileAttributeView("basic")) + throw new RuntimeException("BasicFileAttributeView should be supported"); + + // Test: ClosedFileSystemException + fs.close(); + if (fs.isOpen()) + throw new RuntimeException("FileSystem should be closed"); + try { + fs.getPath("/missing").checkAccess(AccessMode.READ); + } catch (ClosedFileSystemException x) { } + } + + // FileVisitor that pretty prints a file tree + static class FileTreePrinter extends SimpleFileVisitor<Path> { + private int indent = 0; + + private void indent() { + StringBuilder sb = new StringBuilder(indent); + for (int i=0; i<indent; i++) sb.append(" "); + System.out.print(sb); + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) + { + if (dir.getName() != null) { + indent(); + System.out.println(dir.getName() + "/"); + indent++; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) + { + indent(); + System.out.print(file.getName()); + if (attrs.isRegularFile()) + System.out.format(" (%d)", attrs.size()); + System.out.println(); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException + { + if (exc != null) + super.postVisitDirectory(dir, exc); + if (dir.getName() != null) + indent--; + return FileVisitResult.CONTINUE; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/demo/zipfs/PathOps.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.*; +import java.net.*; +import java.util.*; +import java.io.IOException; + +/** + * Tests path operations for zip provider. + */ + +public class PathOps { + + static final java.io.PrintStream out = System.out; + static FileSystem fs; + + private String input; + private Path path; + private Exception exc; + + private PathOps(String s) { + out.println(); + input = s; + try { + path = fs.getPath(s); + out.format("%s -> %s", s, path); + } catch (Exception x) { + exc = x; + out.format("%s -> %s", s, x); + } + out.println(); + } + + Path path() { + return path; + } + + void fail() { + throw new RuntimeException("PathOps failed"); + } + + void checkPath() { + if (path == null) { + throw new InternalError("path is null"); + } + } + + void check(Object result, String expected) { + out.format("\tExpected: %s\n", expected); + out.format("\tActual: %s\n", result); + if (result == null) { + if (expected == null) return; + } else { + // compare string representations + if (expected != null && result.toString().equals(expected.toString())) + return; + } + fail(); + } + + void check(Object result, boolean expected) { + check(result, Boolean.toString(expected)); + } + + PathOps root(String expected) { + out.println("check root"); + checkPath(); + check(path.getRoot(), expected); + return this; + } + + PathOps parent(String expected) { + out.println("check parent"); + checkPath(); + check(path.getParent(), expected); + return this; + } + + PathOps name(String expected) { + out.println("check name"); + checkPath(); + check(path.getName(), expected); + return this; + } + + PathOps element(int index, String expected) { + out.format("check element %d\n", index); + checkPath(); + check(path.getName(index), expected); + return this; + } + + PathOps subpath(int startIndex, int endIndex, String expected) { + out.format("test subpath(%d,%d)\n", startIndex, endIndex); + checkPath(); + check(path.subpath(startIndex, endIndex), expected); + return this; + } + + PathOps starts(String prefix) { + out.format("test startsWith with %s\n", prefix); + checkPath(); + Path s = fs.getPath(prefix); + check(path.startsWith(s), true); + return this; + } + + PathOps notStarts(String prefix) { + out.format("test not startsWith with %s\n", prefix); + checkPath(); + Path s = fs.getPath(prefix); + check(path.startsWith(s), false); + return this; + } + + PathOps ends(String suffix) { + out.format("test endsWith %s\n", suffix); + checkPath(); + Path s = fs.getPath(suffix); + check(path.endsWith(s), true); + return this; + } + + PathOps notEnds(String suffix) { + out.format("test not endsWith %s\n", suffix); + checkPath(); + Path s = fs.getPath(suffix); + check(path.endsWith(s), false); + return this; + } + + PathOps absolute() { + out.println("check path is absolute"); + checkPath(); + check(path.isAbsolute(), true); + return this; + } + + PathOps notAbsolute() { + out.println("check path is not absolute"); + checkPath(); + check(path.isAbsolute(), false); + return this; + } + + PathOps resolve(String other, String expected) { + out.format("test resolve %s\n", other); + checkPath(); + check(path.resolve(other), expected); + return this; + } + + PathOps relativize(String other, String expected) { + out.format("test relativize %s\n", other); + checkPath(); + Path that = fs.getPath(other); + check(path.relativize(that), expected); + return this; + } + + PathOps normalize(String expected) { + out.println("check normalized path"); + checkPath(); + check(path.normalize(), expected); + return this; + } + + PathOps string(String expected) { + out.println("check string representation"); + checkPath(); + check(path, expected); + return this; + } + + PathOps invalid() { + if (!(exc instanceof InvalidPathException)) { + out.println("InvalidPathException not thrown as expected"); + fail(); + } + return this; + } + + static PathOps test(String s) { + return new PathOps(s); + } + + // -- PathOpss -- + + static void header(String s) { + out.println(); + out.println(); + out.println("-- " + s + " --"); + } + + static void doPathOpTests() { + header("Path operations"); + + // all components + test("/a/b/c") + .root("/") + .parent("/a/b") + .name("c"); + + // root component only + test("/") + .root("/") + .parent(null) + .name(null); + + // no root component + test("a/b") + .root(null) + .parent("a") + .name("b"); + + // name component only + test("foo") + .root(null) + .parent(null) + .name("foo"); + + // startsWith + test("/") + .starts("/") + .notStarts("/foo"); + test("/foo") + .starts("/") + .starts("/foo") + .notStarts("/f"); + test("/foo/bar") + .starts("/") + .starts("/foo") + .starts("/foo/bar") + .notStarts("/f") + .notStarts("foo") + .notStarts("foo/bar"); + test("foo") + .starts("foo") + .notStarts("f"); + test("foo/bar") + .starts("foo") + .starts("foo/bar") + .notStarts("f") + .notStarts("/foo") + .notStarts("/foo/bar"); + + // endsWith + test("/") + .ends("/") + .notEnds("foo") + .notEnds("/foo"); + test("/foo") + .ends("foo") + .ends("/foo") + .notEnds("/"); + test("/foo/bar") + .ends("bar") + .ends("foo/bar") + .ends("/foo/bar") + .notEnds("/bar"); + test("foo") + .ends("foo"); + test("foo/bar") + .ends("bar") + .ends("foo/bar"); + + // elements + test("a/b/c") + .element(0,"a") + .element(1,"b") + .element(2,"c"); + + // isAbsolute + test("/") + .absolute(); + test("/tmp") + .absolute(); + test("tmp") + .notAbsolute(); + + // resolve + test("/tmp") + .resolve("foo", "/tmp/foo") + .resolve("/foo", "/foo"); + test("tmp") + .resolve("foo", "tmp/foo") + .resolve("/foo", "/foo"); + + // relativize + test("/a/b/c") + .relativize("/a/b/c", null) + .relativize("/a/b/c/d/e", "d/e") + .relativize("/a/x", "../../x"); + + // normalize + test("/") + .normalize("/"); + test("foo") + .normalize("foo"); + test("/foo") + .normalize("/foo"); + test(".") + .normalize(null); + test("..") + .normalize(".."); + test("/..") + .normalize("/"); + test("/../..") + .normalize("/"); + test("foo/.") + .normalize("foo"); + test("./foo") + .normalize("foo"); + test("foo/..") + .normalize(null); + test("../foo") + .normalize("../foo"); + test("../../foo") + .normalize("../../foo"); + test("foo/bar/..") + .normalize("foo"); + test("foo/bar/gus/../..") + .normalize("foo"); + test("/foo/bar/gus/../..") + .normalize("/foo"); + + // invalid + test("foo\u0000bar") + .invalid(); + test("\u0000foo") + .invalid(); + test("bar\u0000") + .invalid(); + test("//foo\u0000bar") + .invalid(); + test("//\u0000foo") + .invalid(); + test("//bar\u0000") + .invalid(); + + // normalization + test("//foo//bar") + .string("/foo/bar") + .root("/") + .parent("/foo") + .name("bar"); + } + + static void npes() { + header("NullPointerException"); + + Path path = fs.getPath("foo"); + + try { + path.resolve((String)null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.relativize(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.compareTo(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.startsWith(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.endsWith(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + } + + public static void main(String[] args) throws Throwable { + + Path zipfile = Paths.get(args[0]); + Map<String,?> env = new HashMap<String,Object>(); + fs = FileSystems.newFileSystem(zipfile, env, null); + npes(); + doPathOpTests(); + fs.close(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/demo/zipfs/ZipFSTester.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.net.*; +import java.util.*; + +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + +/* + * Tests various zipfs operations. + */ + +public class ZipFSTester { + + public static void main(String[] args) throws Throwable { + FileSystem fs = null; + try { + fs = newZipFileSystem(Paths.get(args[0]), new HashMap<String, Object>()); + test(fs); + test2(fs); // more tests + } finally { + if (fs != null) + fs.close(); + } + } + + static void test(FileSystem fs) + throws Exception + { + Random rdm = new Random(); + + // clone a fs and test on it + Path tmpfsPath = getTempPath(); + Map<String, Object> env = new HashMap<String, Object>(); + env.put("createNew", true); + FileSystem fs0 = newZipFileSystem(tmpfsPath, env); + z2zcopy(fs, fs0, "/", 0); + fs0.close(); // sync to file + + fs = newZipFileSystem(tmpfsPath, new HashMap<String, Object>()); + + try { + // prepare a src + Path src = getTempPath(); + String tmpName = src.toString(); + OutputStream os = src.newOutputStream(); + byte[] bits = new byte[12345]; + rdm.nextBytes(bits); + os.write(bits); + os.close(); + + // copyin + Path dst = getPathWithParents(fs, tmpName); + src.copyTo(dst); + checkEqual(src, dst); + + // copy + Path dst2 = getPathWithParents(fs, "/xyz" + rdm.nextInt(100) + + "/efg" + rdm.nextInt(100) + "/foo.class"); + dst.copyTo(dst2); + //dst.moveTo(dst2); + checkEqual(src, dst2); + + // delete + dst.delete(); + if (dst.exists()) + throw new RuntimeException("Failed!"); + + // moveout + Path dst3 = Paths.get(tmpName + "_Tmp"); + dst2.moveTo(dst3); + checkEqual(src, dst3); + + // delete + if (dst2.exists()) + throw new RuntimeException("Failed!"); + dst3.delete(); + if (dst3.exists()) + throw new RuntimeException("Failed!"); + + // newInputStream on dir + Path parent = dst2.getParent(); + try { + parent.newInputStream(); + throw new RuntimeException("Failed"); + } catch (FileSystemException e) { + e.printStackTrace(); // expected fse + } + + // rmdirs + try { + rmdirs(parent); + } catch (IOException x) { + x.printStackTrace(); + } + + // newFileChannel() copy in, out and verify via fch + fchCopy(src, dst); // in + checkEqual(src, dst); + Path tmp = Paths.get(tmpName + "_Tmp"); + fchCopy(dst, tmp); // out + checkEqual(src, tmp); + tmp.delete(); + + // test channels + channel(fs, dst); + dst.delete(); + src.delete(); + } finally { + if (fs != null) + fs.close(); + if (tmpfsPath.exists()) + tmpfsPath.delete(); + } + } + + static void test2(FileSystem fs) throws Exception { + + Path fs1Path = getTempPath(); + Path fs2Path = getTempPath(); + Path fs3Path = getTempPath(); + + if (fs1Path.exists()) + fs1Path.delete(); + if (fs2Path.exists()) + fs2Path.delete(); + if (fs3Path.exists()) + fs3Path.delete(); + + // create a new filesystem, copy everything from fs + Map<String, Object> env = new HashMap<String, Object>(); + env.put("createNew", true); + FileSystem fs0 = newZipFileSystem(fs1Path, env); + + final FileSystem fs2 = newZipFileSystem(fs2Path, env); + final FileSystem fs3 = newZipFileSystem(fs3Path, env); + + System.out.println("copy src: fs -> fs0..."); + z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 + fs0.close(); // dump to file + + System.out.println("open fs0 as fs1"); + env = new HashMap<String, Object>(); + final FileSystem fs1 = newZipFileSystem(fs1Path, env); + + System.out.println("listing..."); + final ArrayList<String> files = new ArrayList<>(); + final ArrayList<String> dirs = new ArrayList<>(); + list(fs1.getPath("/"), files, dirs); + + Thread t0 = new Thread(new Runnable() { + public void run() { + List<String> list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 0); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t1 = new Thread(new Runnable() { + public void run() { + List<String> list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 1); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t2 = new Thread(new Runnable() { + public void run() { + List<String> list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 2); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t3 = new Thread(new Runnable() { + public void run() { + List<String> list = new ArrayList<>(files); + Collections.shuffle(list); + while (!list.isEmpty()) { + Iterator<String> itr = list.iterator(); + while (itr.hasNext()) { + String path = itr.next(); + try { + if (fs2.getPath(path).exists()) { + z2zmove(fs2, fs3, path); + itr.remove(); + } + } catch (FileAlreadyExistsException x){ + itr.remove(); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + } + + }); + + System.out.println("copying/removing..."); + t0.start(); t1.start(); t2.start(); t3.start(); + t0.join(); t1.join(); t2.join(); t3.join(); + + System.out.println("closing: fs1, fs2"); + fs1.close(); + fs2.close(); + + int failed = 0; + System.out.println("checkEqual: fs vs fs3"); + for (String path : files) { + try { + checkEqual(fs.getPath(path), fs3.getPath(path)); + } catch (IOException x) { + //x.printStackTrace(); + failed++; + } + } + System.out.println("closing: fs3"); + fs3.close(); + + System.out.println("opening: fs3 as fs4"); + FileSystem fs4 = newZipFileSystem(fs3Path, env); + + + ArrayList<String> files2 = new ArrayList<>(); + ArrayList<String> dirs2 = new ArrayList<>(); + list(fs4.getPath("/"), files2, dirs2); + + System.out.println("checkEqual: fs vs fs4"); + for (String path : files2) { + checkEqual(fs.getPath(path), fs4.getPath(path)); + } + System.out.println("walking: fs4"); + walk(fs4.getPath("/")); + System.out.println("closing: fs4"); + fs4.close(); + + System.out.printf("failed=%d%n", failed); + + fs1Path.delete(); + fs2Path.delete(); + fs3Path.delete(); + } + + private static FileSystem newZipFileSystem(Path path, Map<String, ?> env) + throws IOException + { + return FileSystems.newFileSystem( + URI.create("zip" + + path.toUri().toString().substring(4)), + env, + null); + } + + private static Path getTempPath() throws IOException + { + File tmp = File.createTempFile("testzipfs_", "zip"); + tmp.delete(); // we need a clean path, no file + return tmp.toPath(); + } + + private static void list(Path path, List<String> files, List<String> dirs ) + throws IOException + { + if (Attributes.readBasicFileAttributes(path).isDirectory()) { + DirectoryStream<Path> ds = path.newDirectoryStream(); + for (Path child : ds) + list(child, files, dirs); + ds.close(); + dirs.add(path.toString()); + } else { + files.add(path.toString()); + } + } + + private static void z2zcopy(FileSystem src, FileSystem dst, String path, + int method) + throws IOException + { + Path srcPath = src.getPath(path); + Path dstPath = dst.getPath(path); + + if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) { + if (!dstPath.exists()) { + try { + mkdirs(dstPath); + } catch (FileAlreadyExistsException x) {} + } + DirectoryStream<Path> ds = srcPath.newDirectoryStream(); + for (Path child : ds) { + z2zcopy(src, dst, + path + (path.endsWith("/")?"":"/") + child.getName(), + method); + } + ds.close(); + } else { + try { + if (dstPath.exists()) + return; + switch (method) { + case 0: + srcPath.copyTo(dstPath); + break; + case 1: + chCopy(srcPath, dstPath); + break; + case 2: + //fchCopy(srcPath, dstPath); + streamCopy(srcPath, dstPath); + break; + } + } catch (FileAlreadyExistsException x) {} + } + } + + private static void z2zmove(FileSystem src, FileSystem dst, String path) + throws IOException + { + Path srcPath = src.getPath(path); + Path dstPath = dst.getPath(path); + + if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) { + if (!dstPath.exists()) + mkdirs(dstPath); + DirectoryStream<Path> ds = srcPath.newDirectoryStream(); + for (Path child : ds) { + z2zmove(src, dst, + path + (path.endsWith("/")?"":"/") + child.getName()); + } + ds.close(); + } else { + //System.out.println("moving..." + path); + Path parent = dstPath.getParent(); + if (parent != null && parent.notExists()) + mkdirs(parent); + srcPath.moveTo(dstPath); + } + } + + private static void walk(Path path) throws IOException + { + Files.walkFileTree( + path, + new SimpleFileVisitor<Path>() { + private int indent = 0; + private void indent() { + int n = 0; + while (n++ < indent) + System.out.printf(" "); + } + + @Override + public FileVisitResult visitFile(Path file, + BasicFileAttributes attrs) + { + indent(); + System.out.printf("%s%n", file.getName().toString()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, + BasicFileAttributes attrs) + { + indent(); + System.out.printf("[%s]%n", dir.toString()); + indent += 2; + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, + IOException ioe) + throws IOException + { + indent -= 2; + return FileVisitResult.CONTINUE; + } + }); + } + + private static void mkdirs(Path path) throws IOException { + path = path.toAbsolutePath(); + Path parent = path.getParent(); + if (parent != null) { + if (parent.notExists()) + mkdirs(parent); + } + path.createDirectory(); + } + + private static void rmdirs(Path path) throws IOException { + while (path != null && path.getNameCount() != 0) { + path.delete(); + path = path.getParent(); + } + } + + // check the content of two paths are equal + private static void checkEqual(Path src, Path dst) throws IOException + { + //System.out.printf("checking <%s> vs <%s>...%n", + // src.toString(), dst.toString()); + + //streams + InputStream isSrc = src.newInputStream(); + InputStream isDst = dst.newInputStream(); + byte[] bufSrc = new byte[8192]; + byte[] bufDst = new byte[8192]; + + try { + int nSrc = 0; + while ((nSrc = isSrc.read(bufSrc)) != -1) { + int nDst = 0; + while (nDst < nSrc) { + int n = isDst.read(bufDst, nDst, nSrc - nDst); + if (n == -1) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nDst += n; + } + while (--nSrc >= 0) { + if (bufSrc[nSrc] != bufDst[nSrc]) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nSrc--; + } + } + } finally { + isSrc.close(); + isDst.close(); + } + + // channels + SeekableByteChannel chSrc = src.newByteChannel(); + SeekableByteChannel chDst = dst.newByteChannel(); + if (chSrc.size() != chDst.size()) { + System.out.printf("src[%s].size=%d, dst[%s].size=%d%n", + chSrc.toString(), chSrc.size(), + chDst.toString(), chDst.size()); + throw new RuntimeException("CHECK FAILED!"); + } + ByteBuffer bbSrc = ByteBuffer.allocate(8192); + ByteBuffer bbDst = ByteBuffer.allocate(8192); + + try { + int nSrc = 0; + while ((nSrc = chSrc.read(bbSrc)) != -1) { + int nDst = chDst.read(bbDst); + if (nSrc != nDst) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + while (--nSrc >= 0) { + if (bbSrc.get(nSrc) != bbDst.get(nSrc)) { + System.out.printf("checking <%s> vs <%s>...%n", + src.toString(), dst.toString()); + throw new RuntimeException("CHECK FAILED!"); + } + nSrc--; + } + bbSrc.flip(); + bbDst.flip(); + } + } catch (IOException x) { + x.printStackTrace(); + } finally { + chSrc.close(); + chDst.close(); + } + } + + private static void fchCopy(Path src, Path dst) throws IOException + { + Set<OpenOption> read = new HashSet<>(); + read.add(READ); + Set<OpenOption> openwrite = new HashSet<>(); + openwrite.add(CREATE_NEW); + openwrite.add(WRITE); + + FileChannel srcFc = src.getFileSystem() + .provider() + .newFileChannel(src, read); + FileChannel dstFc = dst.getFileSystem() + .provider() + .newFileChannel(dst, openwrite); + + try { + ByteBuffer bb = ByteBuffer.allocate(8192); + while (srcFc.read(bb) >= 0) { + bb.flip(); + dstFc.write(bb); + bb.clear(); + } + } finally { + srcFc.close(); + dstFc.close(); + } + } + + private static void chCopy(Path src, Path dst) throws IOException + { + Set<OpenOption> read = new HashSet<>(); + read.add(READ); + Set<OpenOption> openwrite = new HashSet<>(); + openwrite.add(CREATE_NEW); + openwrite.add(WRITE); + + SeekableByteChannel srcCh = src.newByteChannel(read); + SeekableByteChannel dstCh = dst.newByteChannel(openwrite); + + try { + ByteBuffer bb = ByteBuffer.allocate(8192); + while (srcCh.read(bb) >= 0) { + bb.flip(); + dstCh.write(bb); + bb.clear(); + } + } finally { + srcCh.close(); + dstCh.close(); + } + } + + private static void streamCopy(Path src, Path dst) throws IOException + { + InputStream isSrc = src.newInputStream(); + OutputStream osDst = dst.newOutputStream(); + byte[] buf = new byte[8192]; + try { + int n = 0; + while ((n = isSrc.read(buf)) != -1) { + osDst.write(buf, 0, n); + } + } finally { + isSrc.close(); + osDst.close(); + } + } + + static void channel(FileSystem fs, Path path) + throws Exception + { + System.out.println("test ByteChannel..."); + SeekableByteChannel sbc = path.newByteChannel(); + Set<OpenOption> read = new HashSet<>(); + read.add(READ); + System.out.printf(" sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size()); + ByteBuffer bb = ByteBuffer.allocate((int)sbc.size()); + int n = sbc.read(bb); + System.out.printf(" sbc[1]: read=%d, pos=%d, size=%d%n", + n, sbc.position(), sbc.size()); + ByteBuffer bb2 = ByteBuffer.allocate((int)sbc.size()); + int N = 120; + sbc.close(); + + // sbc.position(pos) is not supported in current version + // try the FileChannel + sbc = fs.provider().newFileChannel(path, read); + sbc.position(N); + System.out.printf(" sbc[2]: pos=%d, size=%d%n", + sbc.position(), sbc.size()); + bb2.limit(100); + n = sbc.read(bb2); + System.out.printf(" sbc[3]: read=%d, pos=%d, size=%d%n", + n, sbc.position(), sbc.size()); + System.out.printf(" sbc[4]: bb[%d]=%d, bb1[0]=%d%n", + N, bb.get(N) & 0xff, bb2.get(0) & 0xff); + sbc.close(); + } + + // create parents if does not exist + static Path getPathWithParents(FileSystem fs, String name) + throws Exception + { + Path path = fs.getPath(name); + Path parent = path.getParent(); + if (parent != null && parent.notExists()) + mkdirs(parent); + return path; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/demo/zipfs/basic.sh Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,73 @@ +# +# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# @test +# @bug 6990846 +# @summary Test ZipFileSystem demo +# @build Basic PathOps ZipFSTester +# @run shell basic.sh + +if [ -z "${TESTJAVA}" ]; then + echo "Test must be run with jtreg" + exit 0 +fi + +ZIPFS="${TESTJAVA}/demo/nio/zipfs/zipfs.jar" +if [ ! -r "${ZIPFS}" ]; then + echo "${ZIPFS} not found" + exit 0 +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + CLASSPATH="${TESTCLASSES};${ZIPFS}" + ;; + * ) + CLASSPATH="${TESTCLASSES}:${ZIPFS}" + ;; +esac +export CLASSPATH + +failures=0 + +go() { + echo "" + ${TESTJAVA}/bin/java $1 $2 $3 2>&1 + if [ $? != 0 ]; then failures=`expr $failures + 1`; fi +} + +# Run the tests + +go Basic "${ZIPFS}" +go PathOps "${ZIPFS}" +go ZipFSTester "${ZIPFS}" + +# +# Results +# + +if [ $failures -gt 0 ]; +then echo "$failures tests failed"; +else echo "All tests passed"; +fi +exit $failures
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/Focus/FocusOwnerFrameOnClick/FocusOwnerFrameOnClick.java Wed Oct 13 17:51:41 2010 -0700 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test FocusOwnerFrameOnClick.java %W% %E% + @bug 6886678 + @summary Tests that clicking an owner frame switches focus from its owned window. + @author Anton Tarasov: area=awt.focus + @library ../../regtesthelpers + @build Util + @run main FocusOwnerFrameOnClick +*/ + +import java.awt.*; +import java.awt.event.*; +import java.applet.Applet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.lang.reflect.InvocationTargetException; +import test.java.awt.regtesthelpers.Util; + +public class FocusOwnerFrameOnClick extends Applet { + Robot robot; + Frame frame = new Frame("Frame"); + Window window = new Window(frame); + Button fButton = new Button("fButton"); + Button wButton = new Button("wButton"); + + AtomicBoolean focused = new AtomicBoolean(false); + + public static void main(String[] args) { + FocusOwnerFrameOnClick app = new FocusOwnerFrameOnClick(); + app.init(); + app.start(); + } + + public void init() { + robot = Util.createRobot(); + + frame.setLayout(new FlowLayout()); + frame.setSize(200, 200); + frame.add(fButton); + + window.setLocation(300, 0); + window.add(wButton); + window.pack(); + } + + public void start() { + frame.setVisible(true); + Util.waitForIdle(robot); + + window.setVisible(true); + Util.waitForIdle(robot); + + if (!wButton.hasFocus()) { + if (!Util.trackFocusGained(wButton, new Runnable() { + public void run() { + Util.clickOnComp(wButton, robot); + } + }, 2000, false)) + { + throw new TestErrorException("wButton didn't gain focus on showing"); + } + } + + Runnable clickAction = new Runnable() { + public void run() { + Point loc = fButton.getLocationOnScreen(); + Dimension dim = fButton.getSize(); + + robot.mouseMove(loc.x, loc.y + dim.height + 20); + robot.delay(50); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.delay(50); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + } + }; + + if (!Util.trackWindowGainedFocus(frame, clickAction, 2000, true)) { + throw new TestFailedException("The frame wasn't focused on click"); + } + + System.out.println("Test passed."); + } +} + +/** + * Thrown when the behavior being verified is found wrong. + */ +class TestFailedException extends RuntimeException { + TestFailedException(String msg) { + super("Test failed: " + msg); + } +} + +/** + * Thrown when an error not related to the behavior being verified is encountered. + */ +class TestErrorException extends RuntimeException { + TestErrorException(String msg) { + super("Unexpected error: " + msg); + } +}
--- a/jdk/test/java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Extra.java Tue Oct 12 17:05:28 2010 -0700 +++ b/jdk/test/java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Extra.java Wed Oct 13 17:51:41 2010 -0700 @@ -21,16 +21,15 @@ static final int SHIFT = 1; static final int CTRL = 2; static final int ALT = 3; - static CheckingModifierAdapter adapterTest1; - static CheckingModifierAdapter adapterTest2; - static CheckingModifierAdapter adapterTest3; - static CheckingModifierAdapter adapterTest4; + static CheckingModifierAdapterExtra adapterTest1; + static CheckingModifierAdapterExtra adapterTest2; + static CheckingModifierAdapterExtra adapterTest3; + static CheckingModifierAdapterExtra adapterTest4; static boolean debug = true; //dump all errors (debug) or throw first(under jtreg) exception static boolean autorun = false; //use robot or manual run static int testModifier = NONE; - static int [] mouseButtons; static int [] mouseButtonDownMasks; //an arrays representing a modifiersEx of extra mouse buttons while using ALT/CTRL/SHIFT or none of them @@ -39,7 +38,6 @@ static int [] modifiersExStandardCTRL; static int [] modifiersExStandardALT; - // final static int [] mouseButtons = new int [] {MouseEvent.BUTTON1_MASK, MouseEvent.BUTTON2_MASK, MouseEvent.BUTTON3_MASK}; // BUTTON1, 2, 3 press-release. final static int modifiersStandard = 0; //InputEvent.BUTTON_DOWN_MASK; @@ -56,7 +54,7 @@ if (modifiersEx != curStandardExModifiers[index]){ // System.out.println(">>>>>>>>>>>>>>> Pressed. modifiersEx "+modifiersEx +" : "+!= curStandardExModifiers"); - MessageLogger.reportError("Test failed : Pressed. modifiersEx != curStandardExModifiers"); + MessageLogger.reportError("Test failed : Pressed. modifiersEx != curStandardExModifiers. Got: " + modifiersEx + " , Expected: " + curStandardExModifiers[index]); } //check event.paramString() output @@ -168,7 +166,7 @@ } if (modifiersEx != curStandardExModifiers[index]){ - MessageLogger.reportError("Test failed : Released. modifiersEx != curStandardExModifiers"); + MessageLogger.reportError("Test failed : Released. modifiersEx != curStandardExModifiers. Got: " + modifiersEx + " , Expected: " + curStandardExModifiers[index]); } //check event.paramString() output @@ -191,7 +189,7 @@ } if (modifiersEx != curStandardExModifiers[index]){ - MessageLogger.reportError("Test failed : Clicked. modifiersEx != curStandardExModifiers"); + MessageLogger.reportError("Test failed : Clicked. modifiersEx != curStandardExModifiers. Got: " + modifiersEx + " , Expected: " + curStandardExModifiers[index]); } //check event.paramString() output @@ -275,11 +273,11 @@ this.addMouseListener(adapterTest1); robot.delay(1000); robot.mouseMove(getLocationOnScreen().x + getWidth()/2, getLocationOnScreen().y + getHeight()/2); - for (int i = 3; i< mouseButtons.length; i++){ - System.out.println("testNONE() => " +mouseButtons[i] ); - robot.mousePress(mouseButtons[i]); + for (int i = 3; i< mouseButtonDownMasks.length; i++){ + System.out.println("testNONE() => " +mouseButtonDownMasks[i] ); + robot.mousePress(mouseButtonDownMasks[i]); robot.delay(100); - robot.mouseRelease(mouseButtons[i]); + robot.mouseRelease(mouseButtonDownMasks[i]); } robot.delay(1000); this.removeMouseListener(adapterTest1); @@ -289,12 +287,12 @@ this.addMouseListener(adapterTest2); robot.delay(1000); robot.mouseMove(getLocationOnScreen().x + getWidth()/2, getLocationOnScreen().y + getHeight()/2); - for (int i = 3; i< mouseButtons.length; i++){ + for (int i = 3; i< mouseButtonDownMasks.length; i++){ robot.keyPress(KeyEvent.VK_SHIFT); - System.out.println("testSHIFT() => " +mouseButtons[i] ); - robot.mousePress(mouseButtons[i]); + System.out.println("testSHIFT() => " +mouseButtonDownMasks[i] ); + robot.mousePress(mouseButtonDownMasks[i]); robot.delay(100); - robot.mouseRelease(mouseButtons[i]); + robot.mouseRelease(mouseButtonDownMasks[i]); robot.keyRelease(KeyEvent.VK_SHIFT); } robot.delay(1000); @@ -305,12 +303,12 @@ this.addMouseListener(adapterTest3); robot.delay(1000); robot.mouseMove(getLocationOnScreen().x + getWidth()/2, getLocationOnScreen().y + getHeight()/2); - for (int i = 3; i< mouseButtons.length; i++){ + for (int i = 3; i< mouseButtonDownMasks.length; i++){ robot.keyPress(KeyEvent.VK_CONTROL); - System.out.println("testCTRL() => " +mouseButtons[i] ); - robot.mousePress(mouseButtons[i]); + System.out.println("testCTRL() => " +mouseButtonDownMasks[i] ); + robot.mousePress(mouseButtonDownMasks[i]); robot.delay(100); - robot.mouseRelease(mouseButtons[i]); + robot.mouseRelease(mouseButtonDownMasks[i]); robot.keyRelease(KeyEvent.VK_CONTROL); } robot.delay(1000); @@ -321,12 +319,12 @@ this.addMouseListener(adapterTest4); robot.delay(1000); robot.mouseMove(getLocationOnScreen().x + getWidth()/2, getLocationOnScreen().y + getHeight()/2); - for (int i = 3; i< mouseButtons.length; i++){ + for (int i = 3; i< mouseButtonDownMasks.length; i++){ robot.keyPress(KeyEvent.VK_ALT); - System.out.println("testALT() => " +mouseButtons[i] ); - robot.mousePress(mouseButtons[i]); + System.out.println("testALT() => " +mouseButtonDownMasks[i] ); + robot.mousePress(mouseButtonDownMasks[i]); robot.delay(100); - robot.mouseRelease(mouseButtons[i]); + robot.mouseRelease(mouseButtonDownMasks[i]); robot.keyRelease(KeyEvent.VK_ALT); } robot.delay(1000); @@ -368,52 +366,52 @@ } public static void initAdapters(){ - adapterTest1 = new CheckingModifierAdapter(NONE); - adapterTest2 = new CheckingModifierAdapter(SHIFT); - adapterTest3 = new CheckingModifierAdapter(CTRL); - adapterTest4 = new CheckingModifierAdapter(ALT); + adapterTest1 = new CheckingModifierAdapterExtra(NONE); + adapterTest2 = new CheckingModifierAdapterExtra(SHIFT); + adapterTest3 = new CheckingModifierAdapterExtra(CTRL); + adapterTest4 = new CheckingModifierAdapterExtra(ALT); } public static void initVars(){ - int [] tmp = new int [MouseInfo.getNumberOfButtons()]; - for (int i = 0; i < MouseInfo.getNumberOfButtons(); i++){ - tmp[i] = InputEvent.getMaskForButton(i+1); - // System.out.println("TEST: "+tmp[i]); + //Init the array of the mouse button masks. It will be used for generating mouse events. + mouseButtonDownMasks = new int [MouseInfo.getNumberOfButtons()]; + for (int i = 0; i < mouseButtonDownMasks.length; i++){ + mouseButtonDownMasks[i] = InputEvent.getMaskForButton(i+1); + System.out.println("MouseArray [i] == "+mouseButtonDownMasks[i]); } - mouseButtons = Arrays.copyOf(tmp, tmp.length); - - for (int i = 0; i < mouseButtons.length; i++){ - System.out.println("MouseArray [i] == "+mouseButtons[i]); - } - - mouseButtonDownMasks = Arrays.copyOf(tmp, tmp.length); - // So we need to get the number of extra buttons on the mouse: "MouseInfo.getNumberOfButtons() - 3" // and multyply on 3 because each button will generate three events : PRESS, RELEASE and CLICK. - tmp = new int [(MouseInfo.getNumberOfButtons()-3)*3]; + int [] tmp = new int [(MouseInfo.getNumberOfButtons()-3)*3]; + + //Fill array of expected results for the case when mouse buttons are only used (no-modifier keys) Arrays.fill(tmp, 0); - for (int i = 0, j = 3; i < tmp.length; i = i + 3, j++){ tmp[i] = mouseButtonDownMasks[j]; } modifiersExStandard = Arrays.copyOf(tmp, tmp.length); + //Fill array of expected results for the case when mouse buttons are only used with SHIFT modifier key Arrays.fill(tmp, InputEvent.SHIFT_DOWN_MASK); - for (int i = 0, j = 3; i < MouseInfo.getNumberOfButtons(); i = i + 3, j++){