--- a/jdk/make/common/Release.gmk Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/common/Release.gmk Wed Dec 26 10:08:36 2012 -0500
@@ -366,6 +366,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/example/debug/expr \
com/sun/tools/example/debug/tty \
com/sun/tools/extcheck \
--- a/jdk/make/common/internal/Defs-langtools.gmk Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/common/internal/Defs-langtools.gmk Wed Dec 26 10:08:36 2012 -0500
@@ -35,6 +35,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/javac \
com/sun/tools/javadoc \
com/sun/tools/javah \
--- a/jdk/make/docs/NON_CORE_PKGS.gmk Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/docs/NON_CORE_PKGS.gmk Wed Dec 26 10:08:36 2012 -0500
@@ -76,7 +76,8 @@
JCONSOLE_PKGS = com.sun.tools.jconsole
-TREEAPI_PKGS = com.sun.source.tree \
+TREEAPI_PKGS = com.sunsource.doctree \
+ com.sun.source.tree \
com.sun.source.util
SMARTCARDIO_PKGS = javax.smartcardio
--- a/jdk/make/java/java/FILES_java.gmk Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/java/java/FILES_java.gmk Wed Dec 26 10:08:36 2012 -0500
@@ -322,6 +322,7 @@
java/util/concurrent/CopyOnWriteArrayList.java \
java/util/concurrent/CopyOnWriteArraySet.java \
java/util/concurrent/CountDownLatch.java \
+ java/util/concurrent/CountedCompleter.java \
java/util/concurrent/CyclicBarrier.java \
java/util/concurrent/DelayQueue.java \
java/util/concurrent/Delayed.java \
--- a/jdk/make/jdk/Makefile Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/jdk/Makefile Wed Dec 26 10:08:36 2012 -0500
@@ -1,5 +1,5 @@
#
-# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 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
@@ -23,16 +23,18 @@
# questions.
#
-#
-# Makefile for building all of java
-#
-
BUILDDIR = ..
+PACKAGE = jdk
PRODUCT = jdk
+JAVAC_LINT_OPTIONS=-Xlint:all
include $(BUILDDIR)/common/Defs.gmk
-SUBDIRS = asm
-include $(BUILDDIR)/common/Subdirs.gmk
+#
+# Files to compile
+#
+AUTO_FILES_JAVA_DIRS = jdk
-all build clean clobber::
- $(SUBDIRS-loop)
+#
+# Rules
+#
+include $(BUILDDIR)/common/Classes.gmk
--- a/jdk/make/jdk/asm/Makefile Wed Dec 26 10:07:00 2012 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#
-# Copyright (c) 1995, 2012, 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.
-#
-
-BUILDDIR = ../..
-PACKAGE = jdk.internal.org.objectweb.asm
-PRODUCT = jdk
-JAVAC_LINT_OPTIONS=-Xlint:all
-include $(BUILDDIR)/common/Defs.gmk
-
-#
-# Files to compile
-#
-AUTO_FILES_JAVA_DIRS = jdk/internal/org/objectweb/asm
-
-#
-# Rules
-#
-include $(BUILDDIR)/common/Classes.gmk
--- a/jdk/make/netbeans/jmx/build.properties Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/make/netbeans/jmx/build.properties Wed Dec 26 10:08:36 2012 -0500
@@ -38,6 +38,7 @@
com/sun/jmx/snmp/
jtreg.tests=\
+ com/sun/jmx/ \
com/sun/management/ \
java/lang/management/ \
javax/management/
--- a/jdk/makefiles/CreateJars.gmk Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/makefiles/CreateJars.gmk Wed Dec 26 10:08:36 2012 -0500
@@ -729,6 +729,7 @@
com/sun/source \
com/sun/tools/classfile \
com/sun/tools/doclets \
+ com/sun/tools/doclint \
com/sun/tools/example/debug/expr \
com/sun/tools/example/debug/tty \
com/sun/tools/extcheck \
--- a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java Wed Dec 26 10:08:36 2012 -0500
@@ -210,9 +210,9 @@
* and DialogPeer interfaces.
*/
private LWWindowPeer createDelegatedPeer(Window target, PlatformComponent platformComponent,
- PlatformWindow platformWindow)
+ PlatformWindow platformWindow, LWWindowPeer.PeerType peerType)
{
- LWWindowPeer peer = new LWWindowPeer(target, platformComponent, platformWindow);
+ LWWindowPeer peer = new LWWindowPeer(target, platformComponent, platformWindow, peerType);
targetCreatedPeer(target, peer);
peer.initialize();
return peer;
@@ -222,22 +222,29 @@
public WindowPeer createWindow(Window target) {
PlatformComponent platformComponent = createPlatformComponent();
PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.SIMPLEWINDOW);
- return createDelegatedPeer(target, platformComponent, platformWindow);
+ return createDelegatedPeer(target, platformComponent, platformWindow, LWWindowPeer.PeerType.SIMPLEWINDOW);
}
@Override
public FramePeer createFrame(Frame target) {
PlatformComponent platformComponent = createPlatformComponent();
PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.FRAME);
- return createDelegatedPeer(target, platformComponent, platformWindow);
+ return createDelegatedPeer(target, platformComponent, platformWindow, LWWindowPeer.PeerType.FRAME);
}
public LWWindowPeer createEmbeddedFrame(CEmbeddedFrame target) {
PlatformComponent platformComponent = createPlatformComponent();
- PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.EMBEDDEDFRAME);
- return createDelegatedPeer(target, platformComponent, platformWindow);
+ PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.EMBEDDED_FRAME);
+ return createDelegatedPeer(target, platformComponent, platformWindow, LWWindowPeer.PeerType.EMBEDDED_FRAME);
}
+ public LWWindowPeer createEmbeddedFrame(CViewEmbeddedFrame target) {
+ PlatformComponent platformComponent = createPlatformComponent();
+ PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.VIEW_EMBEDDED_FRAME);
+ return createDelegatedPeer(target, platformComponent, platformWindow, LWWindowPeer.PeerType.VIEW_EMBEDDED_FRAME);
+ }
+
+
CPrinterDialogPeer createCPrinterDialog(CPrinterDialog target) {
PlatformComponent platformComponent = createPlatformComponent();
PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.DIALOG);
@@ -254,7 +261,7 @@
PlatformComponent platformComponent = createPlatformComponent();
PlatformWindow platformWindow = createPlatformWindow(LWWindowPeer.PeerType.DIALOG);
- return createDelegatedPeer(target, platformComponent, platformWindow);
+ return createDelegatedPeer(target, platformComponent, platformWindow, LWWindowPeer.PeerType.DIALOG);
}
@Override
--- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java Wed Dec 26 10:08:36 2012 -0500
@@ -47,7 +47,8 @@
SIMPLEWINDOW,
FRAME,
DIALOG,
- EMBEDDEDFRAME
+ EMBEDDED_FRAME,
+ VIEW_EMBEDDED_FRAME
}
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer");
@@ -108,6 +109,8 @@
private volatile boolean textured;
+ private final PeerType peerType;
+
/**
* Current modal blocker or null.
*
@@ -116,10 +119,11 @@
private LWWindowPeer blocker;
public LWWindowPeer(Window target, PlatformComponent platformComponent,
- PlatformWindow platformWindow)
+ PlatformWindow platformWindow, PeerType peerType)
{
super(target, platformComponent);
this.platformWindow = platformWindow;
+ this.peerType = peerType;
Window owner = target.getOwner();
LWWindowPeer ownerPeer = (owner != null) ? (LWWindowPeer)owner.getPeer() : null;
@@ -275,6 +279,11 @@
@Override
public void setBounds(int x, int y, int w, int h, int op) {
+
+ if((op & NO_EMBEDDED_CHECK) == 0 && getPeerType() == PeerType.VIEW_EMBEDDED_FRAME) {
+ return;
+ }
+
if ((op & SET_CLIENT_SIZE) != 0) {
// SET_CLIENT_SIZE is only applicable to window peers, so handle it here
// instead of pulling 'insets' field up to LWComponentPeer
@@ -1210,6 +1219,10 @@
return this == grabbingWindow;
}
+ public PeerType getPeerType() {
+ return peerType;
+ }
+
@Override
public String toString() {
return super.toString() + " [target is " + getTarget() + "]";
--- a/jdk/src/macosx/classes/sun/lwawt/PlatformWindow.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/PlatformWindow.java Wed Dec 26 10:08:36 2012 -0500
@@ -151,4 +151,6 @@
public long getLayerPtr();
public LWWindowPeer getPeer();
+
+ public boolean isUnderMouse();
}
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CMouseInfoPeer.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CMouseInfoPeer.java Wed Dec 26 10:08:36 2012 -0500
@@ -26,7 +26,6 @@
package sun.lwawt.macosx;
import java.awt.Window;
-
import sun.lwawt.LWMouseInfoPeer;
import sun.lwawt.LWWindowPeer;
@@ -41,10 +40,6 @@
return false;
}
- LWWindowPeer peer = (LWWindowPeer)w.getPeer();
- CPlatformWindow platformWindow = (CPlatformWindow)peer.getPlatformWindow();
- return nativeIsWindowUnderMouse(platformWindow.getNSWindowPtr());
+ return ((LWWindowPeer)w.getPeer()).getPlatformWindow().isUnderMouse();
}
-
- private static native boolean nativeIsWindowUnderMouse(long ptr);
}
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java Wed Dec 26 10:08:36 2012 -0500
@@ -25,16 +25,13 @@
package sun.lwawt.macosx;
-import sun.lwawt.PlatformWindow;
-import sun.lwawt.LWWindowPeer;
-
-import sun.java2d.opengl.CGLLayer;
+import java.awt.*;
+import sun.awt.CausedFocusEvent;
import sun.java2d.SurfaceData;
-
-import sun.awt.CausedFocusEvent;
-
-import java.awt.*;
-
+import sun.java2d.opengl.CGLLayer;
+import sun.lwawt.LWWindowPeer;
+import sun.lwawt.LWWindowPeer.PeerType;
+import sun.lwawt.PlatformWindow;
import sun.util.logging.PlatformLogger;
/*
@@ -134,6 +131,7 @@
// This method should be properly implemented for applets.
// It returns null just as a stub.
+ @Override
public PlatformWindow getTopmostPlatformWindowUnderMouse() { return null; }
@Override
@@ -192,4 +190,13 @@
@Override
public void setModalBlocked(boolean blocked) {}
+
+ /*
+ * The method could not be implemented due to CALayer restrictions.
+ * The exeption enforce clients not to use it.
+ */
+ @Override
+ public boolean isUnderMouse() {
+ throw new RuntimeException("Not implemented");
+ }
}
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java Wed Dec 26 10:08:36 2012 -0500
@@ -26,8 +26,11 @@
package sun.lwawt.macosx;
import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.VolatileImage;
import sun.awt.CGraphicsConfig;
+import sun.awt.CGraphicsEnvironment;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.event.NSEvent;
@@ -37,6 +40,10 @@
public class CPlatformView extends CFRetainedResource {
private native long nativeCreateView(int x, int y, int width, int height, long windowLayerPtr);
+ private static native void nativeSetAutoResizable(long awtView, boolean toResize);
+ private static native int nativeGetNSViewDisplayID(long awtView);
+ private static native Rectangle2D nativeGetLocationOnScreen(long awtView);
+ private static native boolean nativeIsViewUnderMouse(long ptr);
private LWWindowPeer peer;
private SurfaceData surfaceData;
@@ -59,7 +66,7 @@
public long getAWTView() {
return ptr;
- }
+ }
public boolean isOpaque() {
return !peer.isTranslucent();
@@ -158,10 +165,46 @@
}
}
+ public void setAutoResizable(boolean toResize) {
+ nativeSetAutoResizable(this.getAWTView(), toResize);
+ }
+
+ public boolean isUnderMouse() {
+ return nativeIsViewUnderMouse(getAWTView());
+ }
+
+ public GraphicsDevice getGraphicsDevice() {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ CGraphicsEnvironment cge = (CGraphicsEnvironment)ge;
+ int displayID = nativeGetNSViewDisplayID(getAWTView());
+ GraphicsDevice gd = cge.getScreenDevice(displayID);
+ if (gd == null) {
+ // this could possibly happen during device removal
+ // use the default screen device in this case
+ gd = ge.getDefaultScreenDevice();
+ }
+ return gd;
+ }
+
+ public Point getLocationOnScreen() {
+ Rectangle r = nativeGetLocationOnScreen(this.getAWTView()).getBounds();
+ return new Point(r.x, r.y);
+ }
+
// ----------------------------------------------------------------------
// NATIVE CALLBACKS
// ----------------------------------------------------------------------
+ /*
+ * The callback is called only in the embedded case when the view is
+ * automatically resized by the superview.
+ * In normal mode this method is never called.
+ */
+ private void deliverResize(int x, int y, int w, int h) {
+ peer.notifyReshape(x, y, w, h);
+ }
+
+
private void deliverMouseEvent(NSEvent event) {
int x = event.getX();
int y = getBounds().height - event.getY();
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java Wed Dec 26 10:08:36 2012 -0500
@@ -64,8 +64,6 @@
private static native void nativeDispose(long nsWindowPtr);
private static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
- private static native int nativeGetNSWindowDisplayID(long nsWindowPtr);
-
// Loger to report issues happened during execution but that do not affect functionality
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
private static final PlatformLogger focusLogger = PlatformLogger.getLogger("sun.lwawt.macosx.focus.CPlatformWindow");
@@ -211,9 +209,8 @@
private CPlatformResponder responder;
private volatile boolean zoomed = false; // from native perspective
- public CPlatformWindow(final PeerType peerType) {
+ public CPlatformWindow() {
super(0, true);
- assert (peerType == PeerType.SIMPLEWINDOW || peerType == PeerType.DIALOG || peerType == PeerType.FRAME);
}
/*
@@ -429,16 +426,7 @@
@Override
public GraphicsDevice getGraphicsDevice() {
- GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
- CGraphicsEnvironment cge = (CGraphicsEnvironment)ge;
- int displayID = nativeGetNSWindowDisplayID(getNSWindowPtr());
- GraphicsDevice gd = cge.getScreenDevice(displayID);
- if (gd == null) {
- // this could possibly happen during device removal
- // use the default screen device in this case
- gd = ge.getDefaultScreenDevice();
- }
- return gd;
+ return contentView.getGraphicsDevice();
}
@Override // PlatformWindow
@@ -833,6 +821,11 @@
return peer;
}
+ @Override
+ public boolean isUnderMouse() {
+ return contentView.isUnderMouse();
+ }
+
public CPlatformView getContentView() {
return contentView;
}
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPrinterDialogPeer.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPrinterDialogPeer.java Wed Dec 26 10:08:36 2012 -0500
@@ -41,7 +41,7 @@
public CPrinterDialogPeer(CPrinterDialog target, PlatformComponent platformComponent,
PlatformWindow platformWindow)
{
- super(target, platformComponent, platformWindow);
+ super(target, platformComponent, platformWindow, LWWindowPeer.PeerType.DIALOG);
//super(target);
fTarget = target;
super.initialize();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CViewEmbeddedFrame.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+package sun.lwawt.macosx;
+
+import java.awt.AWTKeyStroke;
+import java.awt.Toolkit;
+import java.lang.reflect.InvocationTargetException;
+import sun.awt.EmbeddedFrame;
+import sun.lwawt.LWToolkit;
+import sun.lwawt.LWWindowPeer;
+/*
+ * The CViewEmbeddedFrame class is used in the SWT_AWT bridge.
+ * This is a part of public API and should not be renamed or moved
+ */
+public class CViewEmbeddedFrame extends EmbeddedFrame {
+
+ private final long nsViewPtr;
+
+ private boolean isActive = false;
+
+ public CViewEmbeddedFrame(long nsViewPtr) {
+ this.nsViewPtr = nsViewPtr;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void addNotify() {
+ if (getPeer() == null) {
+ LWToolkit toolkit = (LWToolkit) Toolkit.getDefaultToolkit();
+ setPeer(toolkit.createEmbeddedFrame(this));
+ }
+ super.addNotify();
+ }
+
+ public long getEmbedderHandle() {
+ return nsViewPtr;
+ }
+
+ @Override
+ public void registerAccelerator(AWTKeyStroke awtks) {
+ }
+
+ @Override
+ public void unregisterAccelerator(AWTKeyStroke awtks) {
+ }
+
+ public boolean isParentWindowActive() {
+ return isActive;
+ }
+
+ /*
+ * Synthetic event delivery for focus management
+ */
+ @Override
+ public void synthesizeWindowActivation(boolean activated) {
+ if (isActive != activated) {
+ isActive = activated;
+ ((LWWindowPeer)getPeer()).notifyActivation(activated, null);
+ }
+ }
+
+ /*
+ * Initializes the embedded frame bounds and validates a component.
+ * Designed to be called from the main thread
+ * This method should be called once from the initialization of the SWT_AWT Bridge
+ */
+ @SuppressWarnings("deprecation")
+ public void validateWithBounds(final int x, final int y, final int width, final int height) {
+ try {
+ LWCToolkit.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ ((LWWindowPeer) getPeer()).setBoundsPrivate(0, 0, width, height);
+ validate();
+ setVisible(true);
+ }
+ }, null);
+ } catch (InterruptedException | InvocationTargetException ex) {}
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CViewPlatformEmbeddedFrame.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package sun.lwawt.macosx;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.GraphicsDevice;
+import java.awt.Insets;
+import java.awt.MenuBar;
+import java.awt.Point;
+import java.awt.Window;
+import sun.awt.CausedFocusEvent.Cause;
+import sun.java2d.SurfaceData;
+import sun.lwawt.LWWindowPeer;
+import sun.lwawt.PlatformWindow;
+
+public class CViewPlatformEmbeddedFrame implements PlatformWindow {
+
+ private CPlatformView view;
+ private LWWindowPeer peer;
+ private CViewEmbeddedFrame target;
+ private CPlatformResponder responder;
+
+ @Override // PlatformWindow
+ public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) {
+ this.peer = peer;
+ this.target = (CViewEmbeddedFrame) target;
+ responder = new CPlatformResponder(peer, false);
+
+ view = new CPlatformView();
+ view.initialize(peer, responder);
+
+ CWrapper.NSView.addSubview(this.target.getEmbedderHandle(), view.getAWTView());
+ view.setAutoResizable(true);
+ }
+
+ public long getNSViewPtr() {
+ return view.getAWTView();
+ }
+
+ @Override
+ public long getLayerPtr() {
+ return view.getWindowLayerPtr();
+ }
+
+ @Override
+ public LWWindowPeer getPeer() {
+ return peer;
+ }
+
+ @Override
+ public void dispose() {
+ CWrapper.NSView.removeFromSuperview(view.getAWTView());
+ view.dispose();
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ CWrapper.NSView.setHidden(view.getAWTView(), !visible);
+ }
+
+ @Override
+ public void setTitle(String title) {
+ }
+
+ @Override
+ public void setBounds(int x, int y, int w, int h) {
+ view.setBounds(x, y, w, h);
+ peer.notifyReshape(x, y, w, h);
+ }
+
+ @Override
+ public GraphicsDevice getGraphicsDevice() {
+ return view.getGraphicsDevice();
+ }
+
+ @Override
+ public Point getLocationOnScreen() {
+ return view.getLocationOnScreen();
+ }
+
+ @Override
+ public Insets getInsets() {
+ return new Insets(0, 0, 0, 0);
+ }
+
+ @Override
+ public FontMetrics getFontMetrics(Font f) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
+ public SurfaceData getScreenSurface() {
+ return view.getSurfaceData();
+ }
+
+ @Override
+ public SurfaceData replaceSurfaceData() {
+ return view.replaceSurfaceData();
+ }
+
+ @Override
+ public void setModalBlocked(boolean blocked) {
+ }
+
+ @Override
+ public void toFront() {
+ }
+
+ @Override
+ public void toBack() {
+ }
+
+ @Override
+ public void setMenuBar(MenuBar mb) {
+ }
+
+ @Override
+ public void setAlwaysOnTop(boolean value) {
+ }
+
+ @Override
+ public PlatformWindow getTopmostPlatformWindowUnderMouse() {
+ return null;
+ }
+
+ @Override
+ public void updateFocusableWindowState() {
+ }
+
+ @Override
+ public boolean rejectFocusRequest(Cause cause) {
+ return false;
+ }
+
+ @Override
+ public boolean requestWindowFocus() {
+ return true;
+ }
+
+ @Override
+ public boolean isActive() {
+ return target.isParentWindowActive();
+ }
+
+ @Override
+ public void setResizable(boolean resizable) {
+ }
+
+ @Override
+ public void setSizeConstraints(int minW, int minH, int maxW, int maxH) {
+ }
+
+ @Override
+ public Graphics transformGraphics(Graphics g) {
+ return g;
+ }
+
+ @Override
+ public void updateIconImages() {
+ }
+
+ @Override
+ public void setOpacity(float opacity) {
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ }
+
+ @Override
+ public void enterFullScreenMode() {
+ }
+
+ @Override
+ public void exitFullScreenMode() {
+ }
+
+ @Override
+ public void setWindowState(int windowState) {
+ }
+
+ @Override
+ public boolean isUnderMouse() {
+ return view.isUnderMouse();
+ }
+}
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CWrapper.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CWrapper.java Wed Dec 26 10:08:36 2012 -0500
@@ -85,6 +85,8 @@
public static native void enterFullScreenMode(long view);
public static native void exitFullScreenMode(long view);
+
+ public static native void setHidden(long view, boolean hidden);
}
public static final class NSObject {
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java Wed Dec 26 10:08:36 2012 -0500
@@ -156,10 +156,13 @@
@Override
protected PlatformWindow createPlatformWindow(PeerType peerType) {
- if (peerType == PeerType.EMBEDDEDFRAME) {
+ if (peerType == PeerType.EMBEDDED_FRAME) {
return new CPlatformEmbeddedFrame();
+ } else if (peerType == PeerType.VIEW_EMBEDDED_FRAME) {
+ return new CViewPlatformEmbeddedFrame();
} else {
- return new CPlatformWindow(peerType);
+ assert (peerType == PeerType.SIMPLEWINDOW || peerType == PeerType.DIALOG || peerType == PeerType.FRAME);
+ return new CPlatformWindow();
}
}
--- a/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m Wed Dec 26 10:08:36 2012 -0500
@@ -99,17 +99,16 @@
__block AWTSurfaceLayers *surfaceLayers = nil;
JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
- AWT_ASSERT_APPKIT_THREAD;
-
- CALayer *windowLayer = jlong_to_ptr(windowLayerPtr);
- surfaceLayers = [[AWTSurfaceLayers alloc] initWithWindowLayer: windowLayer];
- CFRetain(surfaceLayers);
- [surfaceLayers release];
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ CALayer *windowLayer = jlong_to_ptr(windowLayerPtr);
+ surfaceLayers = [[AWTSurfaceLayers alloc] initWithWindowLayer: windowLayer];
+ CFRetain(surfaceLayers);
+ [surfaceLayers release];
}];
-
+
JNF_COCOA_EXIT(env);
return ptr_to_jlong(surfaceLayers);
@@ -126,12 +125,13 @@
JNF_COCOA_ENTER(env);
AWTSurfaceLayers *surfaceLayers = OBJC(surfaceLayersPtr);
- [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
AWT_ASSERT_APPKIT_THREAD;
CGRect rect = CGRectMake(x, y, width, height);
[surfaceLayers setBounds: rect];
- }];
+ }];
JNF_COCOA_EXIT(env);
}
--- a/jdk/src/macosx/native/sun/awt/AWTView.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/AWTView.m Wed Dec 26 10:08:36 2012 -0500
@@ -83,6 +83,7 @@
mouseIsOver = NO;
[self resetTrackingArea];
+ [self setAutoresizesSubviews:NO];
if (windowLayer != nil) {
self.cglLayer = windowLayer;
@@ -174,6 +175,11 @@
* Automatically triggered functions.
*/
+- (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize {
+ [super resizeWithOldSuperviewSize: oldBoundsSize];
+ [self deliverResize: [self frame]];
+}
+
/*
* MouseEvents support
*/
@@ -437,6 +443,18 @@
}
}
+-(void) deliverResize: (NSRect) rect {
+ jint x = (jint) rect.origin.x;
+ jint y = (jint) rect.origin.y;
+ jint w = (jint) rect.size.width;
+ jint h = (jint) rect.size.height;
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ static JNF_CLASS_CACHE(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
+ static JNF_MEMBER_CACHE(jm_deliverResize, jc_PlatformView, "deliverResize", "(IIII)V");
+ JNFCallVoidMethod(env, m_cPlatformView, jm_deliverResize, x,y,w,h);
+}
+
+
- (void) drawRect:(NSRect)dirtyRect {
AWT_ASSERT_APPKIT_THREAD;
@@ -1220,21 +1238,19 @@
__block AWTView *newView = nil;
JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
NSRect rect = NSMakeRect(originX, originY, width, height);
jobject cPlatformView = (*env)->NewGlobalRef(env, obj);
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWT_ASSERT_APPKIT_THREAD;
-
+
CALayer *windowLayer = jlong_to_ptr(windowLayerPtr);
AWTView *view = [[AWTView alloc] initWithRect:rect
platformView:cPlatformView
windowLayer:windowLayer];
CFRetain(view);
[view release]; // GC
-
newView = view;
}];
@@ -1242,3 +1258,125 @@
return ptr_to_jlong(newView);
}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformView
+ * Method: nativeSetAutoResizable
+ * Signature: (JZ)V;
+ */
+
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CPlatformView_nativeSetAutoResizable
+(JNIEnv *env, jclass cls, jlong viewPtr, jboolean toResize)
+{
+JNF_COCOA_ENTER(env);
+
+ NSView *view = (NSView *)jlong_to_ptr(viewPtr);
+
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ if (toResize) {
+ [view setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable];
+ } else {
+ [view setAutoresizingMask: NSViewMinYMargin | NSViewMaxXMargin];
+ }
+
+ if ([view superview] != nil) {
+ [[view superview] setAutoresizesSubviews:(BOOL)toResize];
+ }
+
+ }];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformView
+ * Method: nativeGetNSViewDisplayID
+ * Signature: (J)I;
+ */
+
+JNIEXPORT jint JNICALL
+Java_sun_lwawt_macosx_CPlatformView_nativeGetNSViewDisplayID
+(JNIEnv *env, jclass cls, jlong viewPtr)
+{
+ __block jint ret; //CGDirectDisplayID
+
+JNF_COCOA_ENTER(env);
+
+ NSView *view = (NSView *)jlong_to_ptr(viewPtr);
+ NSWindow *window = [view window];
+
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ ret = (jint)[[AWTWindow getNSWindowDisplayID_AppKitThread: window] intValue];
+ }];
+
+JNF_COCOA_EXIT(env);
+
+ return ret;
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformView
+ * Method: nativeGetLocationOnScreen
+ * Signature: (J)Ljava/awt/Rectangle;
+ */
+
+JNIEXPORT jobject JNICALL
+Java_sun_lwawt_macosx_CPlatformView_nativeGetLocationOnScreen
+(JNIEnv *env, jclass cls, jlong viewPtr)
+{
+ jobject jRect = NULL;
+
+JNF_COCOA_ENTER(env);
+
+ __block NSRect rect = NSZeroRect;
+
+ NSView *view = (NSView *)jlong_to_ptr(viewPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ NSRect viewBounds = [view bounds];
+ NSRect frameInWindow = [view convertRect:viewBounds toView:nil];
+ rect = [[view window] convertRectToScreen:frameInWindow];
+ NSRect screenRect = [[NSScreen mainScreen] frame];
+ //Convert coordinates to top-left corner origin
+ rect.origin.y = screenRect.size.height - rect.origin.y - viewBounds.size.height;
+ }];
+ jRect = NSToJavaRect(env, rect);
+
+JNF_COCOA_EXIT(env);
+
+ return jRect;
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformView
+ * Method: nativeIsViewUnderMouse
+ * Signature: (J)Z;
+ */
+
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformView_nativeIsViewUnderMouse
+(JNIEnv *env, jclass clazz, jlong viewPtr)
+{
+ __block jboolean underMouse = JNI_FALSE;
+
+JNF_COCOA_ENTER(env);
+
+ NSView *nsView = OBJC(viewPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ NSPoint ptWindowCoords = [[nsView window] mouseLocationOutsideOfEventStream];
+ NSPoint ptViewCoords = [nsView convertPoint:ptWindowCoords fromView:nil];
+ underMouse = [nsView hitTest:ptViewCoords] != nil;
+ }];
+
+JNF_COCOA_EXIT(env);
+
+ return underMouse;
+}
+
+
--- a/jdk/src/macosx/native/sun/awt/AWTWindow.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/AWTWindow.m Wed Dec 26 10:08:36 2012 -0500
@@ -1156,34 +1156,6 @@
/*
* Class: sun_lwawt_macosx_CPlatformWindow
- * Method: nativeGetDisplayID_AppKitThread
- * Signature: (J)I
- */
-JNIEXPORT jint JNICALL
-Java_sun_lwawt_macosx_CPlatformWindow_nativeGetNSWindowDisplayID
-(JNIEnv *env, jclass clazz, jlong windowPtr)
-{
- __block jint ret; // CGDirectDisplayID
-
-JNF_COCOA_ENTER(env);
-
- NSWindow *window = OBJC(windowPtr);
-
- if ([NSThread isMainThread]) {
- ret = (jint)[[AWTWindow getNSWindowDisplayID_AppKitThread: window] intValue];
- } else {
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
- ret = (jint)[[AWTWindow getNSWindowDisplayID_AppKitThread: window] intValue];
- }];
- }
-
-JNF_COCOA_EXIT(env);
-
- return ret;
-}
-
-/*
- * Class: sun_lwawt_macosx_CPlatformWindow
* Method: _toggleFullScreenMode
* Signature: (J)V
*/
@@ -1203,27 +1175,6 @@
JNF_COCOA_EXIT(env);
}
-JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CMouseInfoPeer_nativeIsWindowUnderMouse
-(JNIEnv *env, jclass clazz, jlong windowPtr)
-{
- __block jboolean underMouse = JNI_FALSE;
-
-JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
-
- NSWindow *nsWindow = OBJC(windowPtr);
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^() {
- AWT_ASSERT_APPKIT_THREAD;
-
- NSPoint pt = [nsWindow mouseLocationOutsideOfEventStream];
- underMouse = [[nsWindow contentView] hitTest:pt] != nil;
- }];
-
-JNF_COCOA_EXIT(env);
-
- return underMouse;
-}
-
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetEnabled
(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean isEnabled)
{
--- a/jdk/src/macosx/native/sun/awt/CCursorManager.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/CCursorManager.m Wed Dec 26 10:08:36 2012 -0500
@@ -123,14 +123,15 @@
jobject jpt = NULL;
JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
__block NSPoint pt = NSZeroPoint;
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
- AWT_ASSERT_APPKIT_THREAD;
-
- pt = ConvertNSScreenPoint(env, [NSEvent mouseLocation]);
+
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ pt = ConvertNSScreenPoint(env, [NSEvent mouseLocation]);
}];
+
jpt = NSToJavaPoint(env, pt);
JNF_COCOA_EXIT(env);
--- a/jdk/src/macosx/native/sun/awt/CWrapper.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/CWrapper.m Wed Dec 26 10:08:36 2012 -0500
@@ -651,6 +651,26 @@
}
/*
+ * Class: sun_lwawt_macosx_CWrapper$NSView
+ * Method: setHidden
+ * Signature: (JZ)V
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSView_setHidden
+(JNIEnv *env, jclass cls, jlong viewPtr, jboolean toHide)
+{
+ JNF_COCOA_ENTER(env);
+
+ NSView *view = (NSView *)jlong_to_ptr(viewPtr);
+ [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+ [view setHidden:(BOOL)toHide];
+ }];
+
+ JNF_COCOA_EXIT(env);
+}
+
+
+/*
* Class: sun_lwawt_macosx_CWrapper$NSScreen
* Method: frame
* Signature: (J)Ljava/awt/Rectangle;
--- a/jdk/src/macosx/native/sun/awt/awt.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/awt/awt.m Wed Dec 26 10:08:36 2012 -0500
@@ -95,7 +95,7 @@
CFRelease(busyObserver);
CFRelease(notBusyObserver);
-
+
if (!headless) setBusy(YES);
// Set the java name of the AppKit main thread appropriately.
@@ -367,7 +367,8 @@
CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
CFRunLoopRemoveObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
CFRunLoopRemoveObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
-
+ // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
+ setBusy(NO);
busyObserver = NULL;
notBusyObserver = NULL;
} else {
--- a/jdk/src/macosx/native/sun/java2d/opengl/CGLLayer.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/java2d/opengl/CGLLayer.m Wed Dec 26 10:08:36 2012 -0500
@@ -151,16 +151,15 @@
__block CGLLayer *layer = nil;
JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
JNFJObjectWrapper *javaLayer = [JNFJObjectWrapper wrapperWithJObject:obj withEnv:env];
- [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
- AWT_ASSERT_APPKIT_THREAD;
-
- layer = [[CGLLayer alloc] initWithJavaLayer: javaLayer];
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWT_ASSERT_APPKIT_THREAD;
+
+ layer = [[CGLLayer alloc] initWithJavaLayer: javaLayer];
}];
-
+
JNF_COCOA_EXIT(env);
return ptr_to_jlong(layer);
--- a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h Wed Dec 26 10:08:36 2012 -0500
@@ -139,7 +139,7 @@
+ (JNIEnv*)getJNIEnvUncached;
+ (void)performOnMainThread:(SEL)aSelector onObject:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait awtMode:(BOOL)inAWT;
-
++ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block;
@end
void OSXAPP_SetJavaVM(JavaVM *vm);
--- a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m Wed Dec 26 10:08:36 2012 -0500
@@ -245,6 +245,14 @@
}
}
++ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block {
+ if ([NSThread isMainThread] && wait == YES) {
+ block();
+ } else {
+ [JNFRunLoop performOnMainThreadWaiting:wait withBlock:block];
+ }
+}
+
@end
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java Wed Dec 26 10:08:36 2012 -0500
@@ -115,6 +115,7 @@
// restarted is failed by another thread
throw ioe;
}
+ return;
} else {
state = RE_CONNECTING;
lock.notifyAll();
@@ -195,7 +196,7 @@
if (e instanceof IOException &&
!(e instanceof InterruptedIOException)) {
try {
- restart((IOException)e);
+ gotIOException((IOException)e);
} catch (Exception ee) {
logger.warning("Checker-run",
"Failed to check connection: "+ e);
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Wed Dec 26 10:08:36 2012 -0500
@@ -51,6 +51,7 @@
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
+import java.rmi.UnmarshalException;
public abstract class ClientNotifForwarder {
@@ -594,10 +595,7 @@
}
return nr;
- } catch (ClassNotFoundException e) {
- logger.trace("NotifFetcher.fetchNotifs", e);
- return fetchOneNotif();
- } catch (NotSerializableException e) {
+ } catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
logger.trace("NotifFetcher.fetchNotifs", e);
return fetchOneNotif();
} catch (IOException ioe) {
@@ -619,17 +617,18 @@
timeout. This allows us to skip sequence numbers for
notifications that don't match our filters. Then we ask
for one notification. If that produces a
- ClassNotFoundException or a NotSerializableException, we
- increase our sequence number and ask again. Eventually we
- will either get a successful notification, or a return with
- 0 notifications. In either case we can return a
+ ClassNotFoundException, NotSerializableException or
+ UnmarshalException, we increase our sequence number and ask again.
+ Eventually we will either get a successful notification, or a
+ return with 0 notifications. In either case we can return a
NotificationResult. This algorithm works (albeit less
well) even if the server implementation doesn't optimize a
request for 0 notifications to skip sequence numbers for
notifications that don't match our filters.
- If we had at least one ClassNotFoundException, then we
- must emit a JMXConnectionNotification.LOST_NOTIFS.
+ If we had at least one
+ ClassNotFoundException/NotSerializableException/UnmarshalException,
+ then we must emit a JMXConnectionNotification.LOST_NOTIFS.
*/
private NotificationResult fetchOneNotif() {
ClientNotifForwarder cnf = ClientNotifForwarder.this;
@@ -668,23 +667,20 @@
try {
// 1 notif to skip possible missing class
result = cnf.fetchNotifs(startSequenceNumber, 1, 0L);
+ } catch (ClassNotFoundException | NotSerializableException | UnmarshalException e) {
+ logger.warning("NotifFetcher.fetchOneNotif",
+ "Failed to deserialize a notification: "+e.toString());
+ if (logger.traceOn()) {
+ logger.trace("NotifFetcher.fetchOneNotif",
+ "Failed to deserialize a notification.", e);
+ }
+
+ notFoundCount++;
+ startSequenceNumber++;
} catch (Exception e) {
- if (e instanceof ClassNotFoundException
- || e instanceof NotSerializableException) {
- logger.warning("NotifFetcher.fetchOneNotif",
- "Failed to deserialize a notification: "+e.toString());
- if (logger.traceOn()) {
- logger.trace("NotifFetcher.fetchOneNotif",
- "Failed to deserialize a notification.", e);
- }
-
- notFoundCount++;
- startSequenceNumber++;
- } else {
- if (!shouldStop())
- logger.trace("NotifFetcher.fetchOneNotif", e);
- return null;
- }
+ if (!shouldStop())
+ logger.trace("NotifFetcher.fetchOneNotif", e);
+ return null;
}
}
@@ -692,7 +688,7 @@
final String msg =
"Dropped " + notFoundCount + " notification" +
(notFoundCount == 1 ? "" : "s") +
- " because classes were missing locally";
+ " because classes were missing locally or incompatible";
lostNotifs(msg, notFoundCount);
// Even if result.getEarliestSequenceNumber() is now greater than
// it was initially, meaning some notifs have been dropped
--- a/jdk/src/share/classes/com/sun/jmx/remote/internal/IIOPHelper.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/com/sun/jmx/remote/internal/IIOPHelper.java Wed Dec 26 10:08:36 2012 -0500
@@ -26,13 +26,8 @@
package com.sun.jmx.remote.internal;
import java.util.Properties;
+import java.io.IOException;
import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.rmi.NoSuchObjectException;
-
-import java.util.Properties;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
import java.rmi.NoSuchObjectException;
import java.security.AccessController;
@@ -115,9 +110,10 @@
* Connects the Stub to the given ORB.
*/
public static void connect(Object stub, Object orb)
- throws RemoteException
+ throws IOException
{
- ensureAvailable();
+ if (proxy == null)
+ throw new IOException("Connection to ORB failed, RMI/IIOP not available");
proxy.connect(stub, orb);
}
@@ -125,15 +121,17 @@
* Returns true if the given object is an ORB.
*/
public static boolean isOrb(Object obj) {
- ensureAvailable();
- return proxy.isOrb(obj);
+ return (proxy == null) ? false : proxy.isOrb(obj);
}
/**
* Creates, and returns, a new ORB instance.
*/
- public static Object createOrb(String[] args, Properties props) {
- ensureAvailable();
+ public static Object createOrb(String[] args, Properties props)
+ throws IOException
+ {
+ if (proxy == null)
+ throw new IOException("ORB initialization failed, RMI/IIOP not available");
return proxy.createOrb(args, props);
}
@@ -166,24 +164,27 @@
/**
* Makes a server object ready to receive remote calls
*/
- public static void exportObject(Remote obj) throws RemoteException {
- ensureAvailable();
+ public static void exportObject(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new IOException("RMI object cannot be exported, RMI/IIOP not available");
proxy.exportObject(obj);
}
/**
* Deregisters a server object from the runtime.
*/
- public static void unexportObject(Remote obj) throws NoSuchObjectException {
- ensureAvailable();
+ public static void unexportObject(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new NoSuchObjectException("Object not exported");
proxy.unexportObject(obj);
}
/**
* Returns a stub for the given server object.
*/
- public static Remote toStub(Remote obj) throws NoSuchObjectException {
- ensureAvailable();
+ public static Remote toStub(Remote obj) throws IOException {
+ if (proxy == null)
+ throw new NoSuchObjectException("Object not exported");
return proxy.toStub(obj);
}
}
--- a/jdk/src/share/classes/java/lang/Class.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/Class.java Wed Dec 26 10:08:36 2012 -0500
@@ -506,6 +506,7 @@
* returns {@code false} otherwise.
* @return {@code true} if and only if this class is a synthetic class as
* defined by the Java Language Specification.
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
public boolean isSynthetic() {
--- a/jdk/src/share/classes/java/lang/reflect/Constructor.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/reflect/Constructor.java Wed Dec 26 10:08:36 2012 -0500
@@ -66,6 +66,8 @@
private transient ConstructorRepository genericInfo;
private byte[] annotations;
private byte[] parameterAnnotations;
+ // This is set by the vm at Constructor creation
+ private byte[] typeAnnotations;
// Generics infrastructure
// Accessor for factory
@@ -138,6 +140,8 @@
res.root = this;
// Might as well eagerly propagate this if already present
res.constructorAccessor = constructorAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
@@ -407,6 +411,7 @@
/**
* {@inheritDoc}
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
@Override
--- a/jdk/src/share/classes/java/lang/reflect/Executable.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/reflect/Executable.java Wed Dec 26 10:08:36 2012 -0500
@@ -324,6 +324,7 @@
* @return true if and only if this executable is a synthetic
* construct as defined by
* <cite>The Java™ Language Specification</cite>.
+ * @jls 13.1 The Form of a Binary
*/
public boolean isSynthetic() {
return Modifier.isSynthetic(getModifiers());
--- a/jdk/src/share/classes/java/lang/reflect/Field.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/reflect/Field.java Wed Dec 26 10:08:36 2012 -0500
@@ -80,6 +80,8 @@
// currently only two levels deep (i.e., one root Field and
// potentially many Field objects pointing to it.)
private Field root;
+ // This is set by the vm at Field creation
+ private byte[] typeAnnotations;
// Generics infrastructure
@@ -144,6 +146,8 @@
// Might as well eagerly propagate this if already present
res.fieldAccessor = fieldAccessor;
res.overrideFieldAccessor = overrideFieldAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
--- a/jdk/src/share/classes/java/lang/reflect/Member.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/reflect/Member.java Wed Dec 26 10:08:36 2012 -0500
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2012, 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
@@ -87,6 +87,7 @@
*
* @return true if and only if this member was introduced by
* the compiler.
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
public boolean isSynthetic();
--- a/jdk/src/share/classes/java/lang/reflect/Method.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/lang/reflect/Method.java Wed Dec 26 10:08:36 2012 -0500
@@ -79,7 +79,8 @@
// currently only two levels deep (i.e., one root Method and
// potentially many Method objects pointing to it.)
private Method root;
-
+ // This is set by the vm at Method creation
+ private byte[] typeAnnotations;
// Generics infrastructure
private String getGenericSignature() {return signature;}
@@ -150,6 +151,8 @@
res.root = this;
// Might as well eagerly propagate this if already present
res.methodAccessor = methodAccessor;
+
+ res.typeAnnotations = typeAnnotations;
return res;
}
@@ -497,6 +500,7 @@
/**
* {@inheritDoc}
+ * @jls 13.1 The Form of a Binary
* @since 1.5
*/
@Override
@@ -504,6 +508,22 @@
return super.isSynthetic();
}
+ /**
+ * Returns {@code true} if this method is a default
+ * method; returns {@code false} otherwise.
+ *
+ * A default method is a non-abstract method, that is, a method
+ * with a body, declared in an interface type.
+ *
+ * @return true if and only if this method is a default
+ * method as defined by the Java Language Specification.
+ * @since 1.8
+ */
+ public boolean isDefault() {
+ return (getModifiers() & Modifier.ABSTRACT) == 0 &&
+ getDeclaringClass().isInterface();
+ }
+
// NOTE that there is no synchronization used here. It is correct
// (though not efficient) to generate more than one MethodAccessor
// for a given Method. However, avoiding synchronization will
--- a/jdk/src/share/classes/java/util/Properties.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/util/Properties.java Wed Dec 26 10:08:36 2012 -0500
@@ -1188,7 +1188,7 @@
provider = loadProviderAsService(cl);
if (provider != null)
return provider;
- throw new InternalError("No fallback");
+ return new jdk.internal.util.xml.BasicXmlPropertiesProvider();
}});
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/concurrent/CountedCompleter.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,743 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+package java.util.concurrent;
+
+/**
+ * A {@link ForkJoinTask} with a completion action performed when
+ * triggered and there are no remaining pending
+ * actions. CountedCompleters are in general more robust in the
+ * presence of subtask stalls and blockage than are other forms of
+ * ForkJoinTasks, but are less intuitive to program. Uses of
+ * CountedCompleter are similar to those of other completion based
+ * components (such as {@link java.nio.channels.CompletionHandler})
+ * except that multiple <em>pending</em> completions may be necessary
+ * to trigger the completion action {@link #onCompletion}, not just one.
+ * Unless initialized otherwise, the {@linkplain #getPendingCount pending
+ * count} starts at zero, but may be (atomically) changed using
+ * methods {@link #setPendingCount}, {@link #addToPendingCount}, and
+ * {@link #compareAndSetPendingCount}. Upon invocation of {@link
+ * #tryComplete}, if the pending action count is nonzero, it is
+ * decremented; otherwise, the completion action is performed, and if
+ * this completer itself has a completer, the process is continued
+ * with its completer. As is the case with related synchronization
+ * components such as {@link java.util.concurrent.Phaser Phaser} and
+ * {@link java.util.concurrent.Semaphore Semaphore}, these methods
+ * affect only internal counts; they do not establish any further
+ * internal bookkeeping. In particular, the identities of pending
+ * tasks are not maintained. As illustrated below, you can create
+ * subclasses that do record some or all pending tasks or their
+ * results when needed. As illustrated below, utility methods
+ * supporting customization of completion traversals are also
+ * provided. However, because CountedCompleters provide only basic
+ * synchronization mechanisms, it may be useful to create further
+ * abstract subclasses that maintain linkages, fields, and additional
+ * support methods appropriate for a set of related usages.
+ *
+ * <p>A concrete CountedCompleter class must define method {@link
+ * #compute}, that should in most cases (as illustrated below), invoke
+ * {@code tryComplete()} once before returning. The class may also
+ * optionally override method {@link #onCompletion} to perform an
+ * action upon normal completion, and method {@link
+ * #onExceptionalCompletion} to perform an action upon any exception.
+ *
+ * <p>CountedCompleters most often do not bear results, in which case
+ * they are normally declared as {@code CountedCompleter<Void>}, and
+ * will always return {@code null} as a result value. In other cases,
+ * you should override method {@link #getRawResult} to provide a
+ * result from {@code join(), invoke()}, and related methods. In
+ * general, this method should return the value of a field (or a
+ * function of one or more fields) of the CountedCompleter object that
+ * holds the result upon completion. Method {@link #setRawResult} by
+ * default plays no role in CountedCompleters. It is possible, but
+ * rarely applicable, to override this method to maintain other
+ * objects or fields holding result data.
+ *
+ * <p>A CountedCompleter that does not itself have a completer (i.e.,
+ * one for which {@link #getCompleter} returns {@code null}) can be
+ * used as a regular ForkJoinTask with this added functionality.
+ * However, any completer that in turn has another completer serves
+ * only as an internal helper for other computations, so its own task
+ * status (as reported in methods such as {@link ForkJoinTask#isDone})
+ * is arbitrary; this status changes only upon explicit invocations of
+ * {@link #complete}, {@link ForkJoinTask#cancel}, {@link
+ * ForkJoinTask#completeExceptionally} or upon exceptional completion
+ * of method {@code compute}. Upon any exceptional completion, the
+ * exception may be relayed to a task's completer (and its completer,
+ * and so on), if one exists and it has not otherwise already
+ * completed. Similarly, cancelling an internal CountedCompleter has
+ * only a local effect on that completer, so is not often useful.
+ *
+ * <p><b>Sample Usages.</b>
+ *
+ * <p><b>Parallel recursive decomposition.</b> CountedCompleters may
+ * be arranged in trees similar to those often used with {@link
+ * RecursiveAction}s, although the constructions involved in setting
+ * them up typically vary. Here, the completer of each task is its
+ * parent in the computation tree. Even though they entail a bit more
+ * bookkeeping, CountedCompleters may be better choices when applying
+ * a possibly time-consuming operation (that cannot be further
+ * subdivided) to each element of an array or collection; especially
+ * when the operation takes a significantly different amount of time
+ * to complete for some elements than others, either because of
+ * intrinsic variation (for example I/O) or auxiliary effects such as
+ * garbage collection. Because CountedCompleters provide their own
+ * continuations, other threads need not block waiting to perform
+ * them.
+ *
+ * <p>For example, here is an initial version of a class that uses
+ * divide-by-two recursive decomposition to divide work into single
+ * pieces (leaf tasks). Even when work is split into individual calls,
+ * tree-based techniques are usually preferable to directly forking
+ * leaf tasks, because they reduce inter-thread communication and
+ * improve load balancing. In the recursive case, the second of each
+ * pair of subtasks to finish triggers completion of its parent
+ * (because no result combination is performed, the default no-op
+ * implementation of method {@code onCompletion} is not overridden). A
+ * static utility method sets up the base task and invokes it
+ * (here, implicitly using the {@link ForkJoinPool#commonPool()}).
+ *
+ * <pre> {@code
+ * class MyOperation<E> { void apply(E e) { ... } }
+ *
+ * class ForEach<E> extends CountedCompleter<Void> {
+ *
+ * public static <E> void forEach(E[] array, MyOperation<E> op) {
+ * new ForEach<E>(null, array, op, 0, array.length).invoke();
+ * }
+ *
+ * final E[] array; final MyOperation<E> op; final int lo, hi;
+ * ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.op = op; this.lo = lo; this.hi = hi;
+ * }
+ *
+ * public void compute() { // version 1
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(2); // must set pending count before fork
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).fork(); // left child
+ * }
+ * else if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }}</pre>
+ *
+ * This design can be improved by noticing that in the recursive case,
+ * the task has nothing to do after forking its right task, so can
+ * directly invoke its left task before returning. (This is an analog
+ * of tail recursion removal.) Also, because the task returns upon
+ * executing its left task (rather than falling through to invoke
+ * {@code tryComplete}) the pending count is set to one:
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ * public void compute() { // version 2
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * setPendingCount(1); // only one pending
+ * new ForEach(this, array, op, mid, hi).fork(); // right child
+ * new ForEach(this, array, op, lo, mid).compute(); // direct invoke
+ * }
+ * else {
+ * if (hi > lo)
+ * op.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }
+ * }</pre>
+ *
+ * As a further improvement, notice that the left task need not even
+ * exist. Instead of creating a new one, we can iterate using the
+ * original task, and add a pending count for each fork. Additionally,
+ * because no task in this tree implements an {@link #onCompletion}
+ * method, {@code tryComplete()} can be replaced with {@link
+ * #propagateCompletion}.
+ *
+ * <pre> {@code
+ * class ForEach<E> ...
+ * public void compute() { // version 3
+ * int l = lo, h = hi;
+ * while (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * new ForEach(this, array, op, mid, h).fork(); // right child
+ * h = mid;
+ * }
+ * if (h > l)
+ * op.apply(array[l]);
+ * propagateCompletion();
+ * }
+ * }</pre>
+ *
+ * Additional improvements of such classes might entail precomputing
+ * pending counts so that they can be established in constructors,
+ * specializing classes for leaf steps, subdividing by say, four,
+ * instead of two per iteration, and using an adaptive threshold
+ * instead of always subdividing down to single elements.
+ *
+ * <p><b>Searching.</b> A tree of CountedCompleters can search for a
+ * value or property in different parts of a data structure, and
+ * report a result in an {@link
+ * java.util.concurrent.atomic.AtomicReference AtomicReference} as
+ * soon as one is found. The others can poll the result to avoid
+ * unnecessary work. (You could additionally {@linkplain #cancel
+ * cancel} other tasks, but it is usually simpler and more efficient
+ * to just let them notice that the result is set and if so skip
+ * further processing.) Illustrating again with an array using full
+ * partitioning (again, in practice, leaf tasks will almost always
+ * process more than one element):
+ *
+ * <pre> {@code
+ * class Searcher<E> extends CountedCompleter<E> {
+ * final E[] array; final AtomicReference<E> result; final int lo, hi;
+ * Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.result = result; this.lo = lo; this.hi = hi;
+ * }
+ * public E getRawResult() { return result.get(); }
+ * public void compute() { // similar to ForEach version 3
+ * int l = lo, h = hi;
+ * while (result.get() == null && h >= l) {
+ * if (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * new Searcher(this, array, result, mid, h).fork();
+ * h = mid;
+ * }
+ * else {
+ * E x = array[l];
+ * if (matches(x) && result.compareAndSet(null, x))
+ * quietlyCompleteRoot(); // root task is now joinable
+ * break;
+ * }
+ * }
+ * tryComplete(); // normally complete whether or not found
+ * }
+ * boolean matches(E e) { ... } // return true if found
+ *
+ * public static <E> E search(E[] array) {
+ * return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
+ * }
+ *}}</pre>
+ *
+ * In this example, as well as others in which tasks have no other
+ * effects except to compareAndSet a common result, the trailing
+ * unconditional invocation of {@code tryComplete} could be made
+ * conditional ({@code if (result.get() == null) tryComplete();})
+ * because no further bookkeeping is required to manage completions
+ * once the root task completes.
+ *
+ * <p><b>Recording subtasks.</b> CountedCompleter tasks that combine
+ * results of multiple subtasks usually need to access these results
+ * in method {@link #onCompletion}. As illustrated in the following
+ * class (that performs a simplified form of map-reduce where mappings
+ * and reductions are all of type {@code E}), one way to do this in
+ * divide and conquer designs is to have each subtask record its
+ * sibling, so that it can be accessed in method {@code onCompletion}.
+ * This technique applies to reductions in which the order of
+ * combining left and right results does not matter; ordered
+ * reductions require explicit left/right designations. Variants of
+ * other streamlinings seen in the above examples may also apply.
+ *
+ * <pre> {@code
+ * class MyMapper<E> { E apply(E v) { ... } }
+ * class MyReducer<E> { E apply(E x, E y) { ... } }
+ * class MapReducer<E> extends CountedCompleter<E> {
+ * final E[] array; final MyMapper<E> mapper;
+ * final MyReducer<E> reducer; final int lo, hi;
+ * MapReducer<E> sibling;
+ * E result;
+ * MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ * MyReducer<E> reducer, int lo, int hi) {
+ * super(p);
+ * this.array = array; this.mapper = mapper;
+ * this.reducer = reducer; this.lo = lo; this.hi = hi;
+ * }
+ * public void compute() {
+ * if (hi - lo >= 2) {
+ * int mid = (lo + hi) >>> 1;
+ * MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
+ * MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
+ * left.sibling = right;
+ * right.sibling = left;
+ * setPendingCount(1); // only right is pending
+ * right.fork();
+ * left.compute(); // directly execute left
+ * }
+ * else {
+ * if (hi > lo)
+ * result = mapper.apply(array[lo]);
+ * tryComplete();
+ * }
+ * }
+ * public void onCompletion(CountedCompleter<?> caller) {
+ * if (caller != this) {
+ * MapReducer<E> child = (MapReducer<E>)caller;
+ * MapReducer<E> sib = child.sibling;
+ * if (sib == null || sib.result == null)
+ * result = child.result;
+ * else
+ * result = reducer.apply(child.result, sib.result);
+ * }
+ * }
+ * public E getRawResult() { return result; }
+ *
+ * public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ * return new MapReducer<E>(null, array, mapper, reducer,
+ * 0, array.length).invoke();
+ * }
+ * }}</pre>
+ *
+ * Here, method {@code onCompletion} takes a form common to many
+ * completion designs that combine results. This callback-style method
+ * is triggered once per task, in either of the two different contexts
+ * in which the pending count is, or becomes, zero: (1) by a task
+ * itself, if its pending count is zero upon invocation of {@code
+ * tryComplete}, or (2) by any of its subtasks when they complete and
+ * decrement the pending count to zero. The {@code caller} argument
+ * distinguishes cases. Most often, when the caller is {@code this},
+ * no action is necessary. Otherwise the caller argument can be used
+ * (usually via a cast) to supply a value (and/or links to other
+ * values) to be combined. Assuming proper use of pending counts, the
+ * actions inside {@code onCompletion} occur (once) upon completion of
+ * a task and its subtasks. No additional synchronization is required
+ * within this method to ensure thread safety of accesses to fields of
+ * this task or other completed tasks.
+ *
+ * <p><b>Completion Traversals</b>. If using {@code onCompletion} to
+ * process completions is inapplicable or inconvenient, you can use
+ * methods {@link #firstComplete} and {@link #nextComplete} to create
+ * custom traversals. For example, to define a MapReducer that only
+ * splits out right-hand tasks in the form of the third ForEach
+ * example, the completions must cooperatively reduce along
+ * unexhausted subtask links, which can be done as follows:
+ *
+ * <pre> {@code
+ * class MapReducer<E> extends CountedCompleter<E> { // version 2
+ * final E[] array; final MyMapper<E> mapper;
+ * final MyReducer<E> reducer; final int lo, hi;
+ * MapReducer<E> forks, next; // record subtask forks in list
+ * E result;
+ * MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ * MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
+ * super(p);
+ * this.array = array; this.mapper = mapper;
+ * this.reducer = reducer; this.lo = lo; this.hi = hi;
+ * this.next = next;
+ * }
+ * public void compute() {
+ * int l = lo, h = hi;
+ * while (h - l >= 2) {
+ * int mid = (l + h) >>> 1;
+ * addToPendingCount(1);
+ * (forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork;
+ * h = mid;
+ * }
+ * if (h > l)
+ * result = mapper.apply(array[l]);
+ * // process completions by reducing along and advancing subtask links
+ * for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
+ * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
+ * t.result = reducer.apply(t.result, s.result);
+ * }
+ * }
+ * public E getRawResult() { return result; }
+ *
+ * public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
+ * return new MapReducer<E>(null, array, mapper, reducer,
+ * 0, array.length, null).invoke();
+ * }
+ * }}</pre>
+ *
+ * <p><b>Triggers.</b> Some CountedCompleters are themselves never
+ * forked, but instead serve as bits of plumbing in other designs;
+ * including those in which the completion of one of more async tasks
+ * triggers another async task. For example:
+ *
+ * <pre> {@code
+ * class HeaderBuilder extends CountedCompleter<...> { ... }
+ * class BodyBuilder extends CountedCompleter<...> { ... }
+ * class PacketSender extends CountedCompleter<...> {
+ * PacketSender(...) { super(null, 1); ... } // trigger on second completion
+ * public void compute() { } // never called
+ * public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
+ * }
+ * // sample use:
+ * PacketSender p = new PacketSender();
+ * new HeaderBuilder(p, ...).fork();
+ * new BodyBuilder(p, ...).fork();
+ * }</pre>
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
+ private static final long serialVersionUID = 5232453752276485070L;
+
+ /** This task's completer, or null if none */
+ final CountedCompleter<?> completer;
+ /** The number of pending tasks until completion */
+ volatile int pending;
+
+ /**
+ * Creates a new CountedCompleter with the given completer
+ * and initial pending count.
+ *
+ * @param completer this task's completer, or {@code null} if none
+ * @param initialPendingCount the initial pending count
+ */
+ protected CountedCompleter(CountedCompleter<?> completer,
+ int initialPendingCount) {
+ this.completer = completer;
+ this.pending = initialPendingCount;
+ }
+
+ /**
+ * Creates a new CountedCompleter with the given completer
+ * and an initial pending count of zero.
+ *
+ * @param completer this task's completer, or {@code null} if none
+ */
+ protected CountedCompleter(CountedCompleter<?> completer) {
+ this.completer = completer;
+ }
+
+ /**
+ * Creates a new CountedCompleter with no completer
+ * and an initial pending count of zero.
+ */
+ protected CountedCompleter() {
+ this.completer = null;
+ }
+
+ /**
+ * The main computation performed by this task.
+ */
+ public abstract void compute();
+
+ /**
+ * Performs an action when method {@link #tryComplete} is invoked
+ * and the pending count is zero, or when the unconditional
+ * method {@link #complete} is invoked. By default, this method
+ * does nothing. You can distinguish cases by checking the
+ * identity of the given caller argument. If not equal to {@code
+ * this}, then it is typically a subtask that may contain results
+ * (and/or links to other results) to combine.
+ *
+ * @param caller the task invoking this method (which may
+ * be this task itself).
+ */
+ public void onCompletion(CountedCompleter<?> caller) {
+ }
+
+ /**
+ * Performs an action when method {@link #completeExceptionally}
+ * is invoked or method {@link #compute} throws an exception, and
+ * this task has not otherwise already completed normally. On
+ * entry to this method, this task {@link
+ * ForkJoinTask#isCompletedAbnormally}. The return value of this
+ * method controls further propagation: If {@code true} and this
+ * task has a completer, then this completer is also completed
+ * exceptionally. The default implementation of this method does
+ * nothing except return {@code true}.
+ *
+ * @param ex the exception
+ * @param caller the task invoking this method (which may
+ * be this task itself).
+ * @return true if this exception should be propagated to this
+ * task's completer, if one exists.
+ */
+ public boolean onExceptionalCompletion(Throwable ex, CountedCompleter<?> caller) {
+ return true;
+ }
+
+ /**
+ * Returns the completer established in this task's constructor,
+ * or {@code null} if none.
+ *
+ * @return the completer
+ */
+ public final CountedCompleter<?> getCompleter() {
+ return completer;
+ }
+
+ /**
+ * Returns the current pending count.
+ *
+ * @return the current pending count
+ */
+ public final int getPendingCount() {
+ return pending;
+ }
+
+ /**
+ * Sets the pending count to the given value.
+ *
+ * @param count the count
+ */
+ public final void setPendingCount(int count) {
+ pending = count;
+ }
+
+ /**
+ * Adds (atomically) the given value to the pending count.
+ *
+ * @param delta the value to add
+ */
+ public final void addToPendingCount(int delta) {
+ int c; // note: can replace with intrinsic in jdk8
+ do {} while (!U.compareAndSwapInt(this, PENDING, c = pending, c+delta));
+ }
+
+ /**
+ * Sets (atomically) the pending count to the given count only if
+ * it currently holds the given expected value.
+ *
+ * @param expected the expected value
+ * @param count the new value
+ * @return true if successful
+ */
+ public final boolean compareAndSetPendingCount(int expected, int count) {
+ return U.compareAndSwapInt(this, PENDING, expected, count);
+ }
+
+ /**
+ * If the pending count is nonzero, (atomically) decrements it.
+ *
+ * @return the initial (undecremented) pending count holding on entry
+ * to this method
+ */
+ public final int decrementPendingCountUnlessZero() {
+ int c;
+ do {} while ((c = pending) != 0 &&
+ !U.compareAndSwapInt(this, PENDING, c, c - 1));
+ return c;
+ }
+
+ /**
+ * Returns the root of the current computation; i.e., this
+ * task if it has no completer, else its completer's root.
+ *
+ * @return the root of the current computation
+ */
+ public final CountedCompleter<?> getRoot() {
+ CountedCompleter<?> a = this, p;
+ while ((p = a.completer) != null)
+ a = p;
+ return a;
+ }
+
+ /**
+ * If the pending count is nonzero, decrements the count;
+ * otherwise invokes {@link #onCompletion} and then similarly
+ * tries to complete this task's completer, if one exists,
+ * else marks this task as complete.
+ */
+ public final void tryComplete() {
+ CountedCompleter<?> a = this, s = a;
+ for (int c;;) {
+ if ((c = a.pending) == 0) {
+ a.onCompletion(s);
+ if ((a = (s = a).completer) == null) {
+ s.quietlyComplete();
+ return;
+ }
+ }
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ return;
+ }
+ }
+
+ /**
+ * Equivalent to {@link #tryComplete} but does not invoke {@link
+ * #onCompletion} along the completion path: If the pending count
+ * is nonzero, decrements the count; otherwise, similarly tries to
+ * complete this task's completer, if one exists, else marks this
+ * task as complete. This method may be useful in cases where
+ * {@code onCompletion} should not, or need not, be invoked for
+ * each completer in a computation.
+ */
+ public final void propagateCompletion() {
+ CountedCompleter<?> a = this, s = a;
+ for (int c;;) {
+ if ((c = a.pending) == 0) {
+ if ((a = (s = a).completer) == null) {
+ s.quietlyComplete();
+ return;
+ }
+ }
+ else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ return;
+ }
+ }
+
+ /**
+ * Regardless of pending count, invokes {@link #onCompletion},
+ * marks this task as complete and further triggers {@link
+ * #tryComplete} on this task's completer, if one exists. The
+ * given rawResult is used as an argument to {@link #setRawResult}
+ * before invoking {@link #onCompletion} or marking this task as
+ * complete; its value is meaningful only for classes overriding
+ * {@code setRawResult}.
+ *
+ * <p>This method may be useful when forcing completion as soon as
+ * any one (versus all) of several subtask results are obtained.
+ * However, in the common (and recommended) case in which {@code
+ * setRawResult} is not overridden, this effect can be obtained
+ * more simply using {@code quietlyCompleteRoot();}.
+ *
+ * @param rawResult the raw result
+ */
+ public void complete(T rawResult) {
+ CountedCompleter<?> p;
+ setRawResult(rawResult);
+ onCompletion(this);
+ quietlyComplete();
+ if ((p = completer) != null)
+ p.tryComplete();
+ }
+
+
+ /**
+ * If this task's pending count is zero, returns this task;
+ * otherwise decrements its pending count and returns {@code
+ * null}. This method is designed to be used with {@link
+ * #nextComplete} in completion traversal loops.
+ *
+ * @return this task, if pending count was zero, else {@code null}
+ */
+ public final CountedCompleter<?> firstComplete() {
+ for (int c;;) {
+ if ((c = pending) == 0)
+ return this;
+ else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
+ return null;
+ }
+ }
+
+ /**
+ * If this task does not have a completer, invokes {@link
+ * ForkJoinTask#quietlyComplete} and returns {@code null}. Or, if
+ * this task's pending count is non-zero, decrements its pending
+ * count and returns {@code null}. Otherwise, returns the
+ * completer. This method can be used as part of a completion
+ * traversal loop for homogeneous task hierarchies:
+ *
+ * <pre> {@code
+ * for (CountedCompleter<?> c = firstComplete();
+ * c != null;
+ * c = c.nextComplete()) {
+ * // ... process c ...
+ * }}</pre>
+ *
+ * @return the completer, or {@code null} if none
+ */
+ public final CountedCompleter<?> nextComplete() {
+ CountedCompleter<?> p;
+ if ((p = completer) != null)
+ return p.firstComplete();
+ else {
+ quietlyComplete();
+ return null;
+ }
+ }
+
+ /**
+ * Equivalent to {@code getRoot().quietlyComplete()}.
+ */
+ public final void quietlyCompleteRoot() {
+ for (CountedCompleter<?> a = this, p;;) {
+ if ((p = a.completer) == null) {
+ a.quietlyComplete();
+ return;
+ }
+ a = p;
+ }
+ }
+
+ /**
+ * Supports ForkJoinTask exception propagation.
+ */
+ void internalPropagateException(Throwable ex) {
+ CountedCompleter<?> a = this, s = a;
+ while (a.onExceptionalCompletion(ex, s) &&
+ (a = (s = a).completer) != null && a.status >= 0)
+ a.recordExceptionalCompletion(ex);
+ }
+
+ /**
+ * Implements execution conventions for CountedCompleters.
+ */
+ protected final boolean exec() {
+ compute();
+ return false;
+ }
+
+ /**
+ * Returns the result of the computation. By default
+ * returns {@code null}, which is appropriate for {@code Void}
+ * actions, but in other cases should be overridden, almost
+ * always to return a field or function of a field that
+ * holds the result upon completion.
+ *
+ * @return the result of the computation
+ */
+ public T getRawResult() { return null; }
+
+ /**
+ * A method that result-bearing CountedCompleters may optionally
+ * use to help maintain result data. By default, does nothing.
+ * Overrides are not recommended. However, if this method is
+ * overridden to update existing objects or fields, then it must
+ * in general be defined to be thread-safe.
+ */
+ protected void setRawResult(T t) { }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U;
+ private static final long PENDING;
+ static {
+ try {
+ U = sun.misc.Unsafe.getUnsafe();
+ PENDING = U.objectFieldOffset
+ (CountedCompleter.class.getDeclaredField("pending"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Wed Dec 26 10:08:36 2012 -0500
@@ -40,7 +40,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@@ -48,11 +47,6 @@
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.LockSupport;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Condition;
/**
* An {@link ExecutorService} for running {@link ForkJoinTask}s.
@@ -63,21 +57,31 @@
* <p>A {@code ForkJoinPool} differs from other kinds of {@link
* ExecutorService} mainly by virtue of employing
* <em>work-stealing</em>: all threads in the pool attempt to find and
- * execute subtasks created by other active tasks (eventually blocking
- * waiting for work if none exist). This enables efficient processing
- * when most tasks spawn other subtasks (as do most {@code
- * ForkJoinTask}s). When setting <em>asyncMode</em> to true in
- * constructors, {@code ForkJoinPool}s may also be appropriate for use
- * with event-style tasks that are never joined.
+ * execute tasks submitted to the pool and/or created by other active
+ * tasks (eventually blocking waiting for work if none exist). This
+ * enables efficient processing when most tasks spawn other subtasks
+ * (as do most {@code ForkJoinTask}s), as well as when many small
+ * tasks are submitted to the pool from external clients. Especially
+ * when setting <em>asyncMode</em> to true in constructors, {@code
+ * ForkJoinPool}s may also be appropriate for use with event-style
+ * tasks that are never joined.
*
- * <p>A {@code ForkJoinPool} is constructed with a given target
- * parallelism level; by default, equal to the number of available
- * processors. The pool attempts to maintain enough active (or
- * available) threads by dynamically adding, suspending, or resuming
- * internal worker threads, even if some tasks are stalled waiting to
- * join others. However, no such adjustments are guaranteed in the
- * face of blocked IO or other unmanaged synchronization. The nested
- * {@link ManagedBlocker} interface enables extension of the kinds of
+ * <p>A static {@link #commonPool()} is available and appropriate for
+ * most applications. The common pool is used by any ForkJoinTask that
+ * is not explicitly submitted to a specified pool. Using the common
+ * pool normally reduces resource usage (its threads are slowly
+ * reclaimed during periods of non-use, and reinstated upon subsequent
+ * use).
+ *
+ * <p>For applications that require separate or custom pools, a {@code
+ * ForkJoinPool} may be constructed with a given target parallelism
+ * level; by default, equal to the number of available processors. The
+ * pool attempts to maintain enough active (or available) threads by
+ * dynamically adding, suspending, or resuming internal worker
+ * threads, even if some tasks are stalled waiting to join
+ * others. However, no such adjustments are guaranteed in the face of
+ * blocked I/O or other unmanaged synchronization. The nested {@link
+ * ManagedBlocker} interface enables extension of the kinds of
* synchronization accommodated.
*
* <p>In addition to execution and lifecycle control methods, this
@@ -87,16 +91,17 @@
* {@link #toString} returns indications of pool state in a
* convenient form for informal monitoring.
*
- * <p> As is the case with other ExecutorServices, there are three
- * main task execution methods summarized in the following
- * table. These are designed to be used by clients not already engaged
- * in fork/join computations in the current pool. The main forms of
- * these methods accept instances of {@code ForkJoinTask}, but
- * overloaded forms also allow mixed execution of plain {@code
+ * <p>As is the case with other ExecutorServices, there are three
+ * main task execution methods summarized in the following table.
+ * These are designed to be used primarily by clients not already
+ * engaged in fork/join computations in the current pool. The main
+ * forms of these methods accept instances of {@code ForkJoinTask},
+ * but overloaded forms also allow mixed execution of plain {@code
* Runnable}- or {@code Callable}- based activities as well. However,
- * tasks that are already executing in a pool should normally
- * <em>NOT</em> use these pool execution methods, but instead use the
- * within-computation forms listed in the table.
+ * tasks that are already executing in a pool should normally instead
+ * use the within-computation forms listed in the table unless using
+ * async event-style tasks that are not usually joined, in which case
+ * there is little difference among choice of methods.
*
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <tr>
@@ -121,23 +126,16 @@
* </tr>
* </table>
*
- * <p><b>Sample Usage.</b> Normally a single {@code ForkJoinPool} is
- * used for all parallel task execution in a program or subsystem.
- * Otherwise, use would not usually outweigh the construction and
- * bookkeeping overhead of creating a large set of threads. For
- * example, a common pool could be used for the {@code SortTasks}
- * illustrated in {@link RecursiveAction}. Because {@code
- * ForkJoinPool} uses threads in {@linkplain java.lang.Thread#isDaemon
- * daemon} mode, there is typically no need to explicitly {@link
- * #shutdown} such a pool upon program exit.
- *
- * <pre>
- * static final ForkJoinPool mainPool = new ForkJoinPool();
- * ...
- * public void sort(long[] array) {
- * mainPool.invoke(new SortTask(array, 0, array.length));
- * }
- * </pre>
+ * <p>The common pool is by default constructed with default
+ * parameters, but these may be controlled by setting three {@link
+ * System#getProperty system properties} with prefix {@code
+ * java.util.concurrent.ForkJoinPool.common}: {@code parallelism} --
+ * an integer greater than zero, {@code threadFactory} -- the class
+ * name of a {@link ForkJoinWorkerThreadFactory}, and {@code
+ * exceptionHandler} -- the class name of a {@link
+ * java.lang.Thread.UncaughtExceptionHandler
+ * Thread.UncaughtExceptionHandler}. Upon any error in establishing
+ * these settings, default parameters are used.
*
* <p><b>Implementation notes</b>: This implementation restricts the
* maximum number of running threads to 32767. Attempts to create
@@ -156,214 +154,388 @@
/*
* Implementation Overview
*
- * This class provides the central bookkeeping and control for a
- * set of worker threads: Submissions from non-FJ threads enter
- * into a submission queue. Workers take these tasks and typically
- * split them into subtasks that may be stolen by other workers.
- * Preference rules give first priority to processing tasks from
- * their own queues (LIFO or FIFO, depending on mode), then to
- * randomized FIFO steals of tasks in other worker queues, and
- * lastly to new submissions.
+ * This class and its nested classes provide the main
+ * functionality and control for a set of worker threads:
+ * Submissions from non-FJ threads enter into submission queues.
+ * Workers take these tasks and typically split them into subtasks
+ * that may be stolen by other workers. Preference rules give
+ * first priority to processing tasks from their own queues (LIFO
+ * or FIFO, depending on mode), then to randomized FIFO steals of
+ * tasks in other queues.
+ *
+ * WorkQueues
+ * ==========
+ *
+ * Most operations occur within work-stealing queues (in nested
+ * class WorkQueue). These are special forms of Deques that
+ * support only three of the four possible end-operations -- push,
+ * pop, and poll (aka steal), under the further constraints that
+ * push and pop are called only from the owning thread (or, as
+ * extended here, under a lock), while poll may be called from
+ * other threads. (If you are unfamiliar with them, you probably
+ * want to read Herlihy and Shavit's book "The Art of
+ * Multiprocessor programming", chapter 16 describing these in
+ * more detail before proceeding.) The main work-stealing queue
+ * design is roughly similar to those in the papers "Dynamic
+ * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
+ * (http://research.sun.com/scalable/pubs/index.html) and
+ * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
+ * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
+ * The main differences ultimately stem from GC requirements that
+ * we null out taken slots as soon as we can, to maintain as small
+ * a footprint as possible even in programs generating huge
+ * numbers of tasks. To accomplish this, we shift the CAS
+ * arbitrating pop vs poll (steal) from being on the indices
+ * ("base" and "top") to the slots themselves. So, both a
+ * successful pop and poll mainly entail a CAS of a slot from
+ * non-null to null. Because we rely on CASes of references, we
+ * do not need tag bits on base or top. They are simple ints as
+ * used in any circular array-based queue (see for example
+ * ArrayDeque). Updates to the indices must still be ordered in a
+ * way that guarantees that top == base means the queue is empty,
+ * but otherwise may err on the side of possibly making the queue
+ * appear nonempty when a push, pop, or poll have not fully
+ * committed. Note that this means that the poll operation,
+ * considered individually, is not wait-free. One thief cannot
+ * successfully continue until another in-progress one (or, if
+ * previously empty, a push) completes. However, in the
+ * aggregate, we ensure at least probabilistic non-blockingness.
+ * If an attempted steal fails, a thief always chooses a different
+ * random victim target to try next. So, in order for one thief to
+ * progress, it suffices for any in-progress poll or new push on
+ * any empty queue to complete. (This is why we normally use
+ * method pollAt and its variants that try once at the apparent
+ * base index, else consider alternative actions, rather than
+ * method poll.)
+ *
+ * This approach also enables support of a user mode in which local
+ * task processing is in FIFO, not LIFO order, simply by using
+ * poll rather than pop. This can be useful in message-passing
+ * frameworks in which tasks are never joined. However neither
+ * mode considers affinities, loads, cache localities, etc, so
+ * rarely provide the best possible performance on a given
+ * machine, but portably provide good throughput by averaging over
+ * these factors. (Further, even if we did try to use such
+ * information, we do not usually have a basis for exploiting it.
+ * For example, some sets of tasks profit from cache affinities,
+ * but others are harmed by cache pollution effects.)
+ *
+ * WorkQueues are also used in a similar way for tasks submitted
+ * to the pool. We cannot mix these tasks in the same queues used
+ * for work-stealing (this would contaminate lifo/fifo
+ * processing). Instead, we randomly associate submission queues
+ * with submitting threads, using a form of hashing. The
+ * ThreadLocal Submitter class contains a value initially used as
+ * a hash code for choosing existing queues, but may be randomly
+ * repositioned upon contention with other submitters. In
+ * essence, submitters act like workers except that they are
+ * restricted to executing local tasks that they submitted (or in
+ * the case of CountedCompleters, others with the same root task).
+ * However, because most shared/external queue operations are more
+ * expensive than internal, and because, at steady state, external
+ * submitters will compete for CPU with workers, ForkJoinTask.join
+ * and related methods disable them from repeatedly helping to
+ * process tasks if all workers are active. Insertion of tasks in
+ * shared mode requires a lock (mainly to protect in the case of
+ * resizing) but we use only a simple spinlock (using bits in
+ * field qlock), because submitters encountering a busy queue move
+ * on to try or create other queues -- they block only when
+ * creating and registering new queues.
+ *
+ * Management
+ * ==========
*
* The main throughput advantages of work-stealing stem from
* decentralized control -- workers mostly take tasks from
* themselves or each other. We cannot negate this in the
* implementation of other management responsibilities. The main
* tactic for avoiding bottlenecks is packing nearly all
- * essentially atomic control state into a single 64bit volatile
- * variable ("ctl"). This variable is read on the order of 10-100
- * times as often as it is modified (always via CAS). (There is
- * some additional control state, for example variable "shutdown"
- * for which we can cope with uncoordinated updates.) This
- * streamlines synchronization and control at the expense of messy
- * constructions needed to repack status bits upon updates.
- * Updates tend not to contend with each other except during
- * bursts while submitted tasks begin or end. In some cases when
- * they do contend, threads can instead do something else
- * (usually, scan for tasks) until contention subsides.
+ * essentially atomic control state into two volatile variables
+ * that are by far most often read (not written) as status and
+ * consistency checks.
*
- * To enable packing, we restrict maximum parallelism to (1<<15)-1
- * (which is far in excess of normal operating range) to allow
- * ids, counts, and their negations (used for thresholding) to fit
- * into 16bit fields.
+ * Field "ctl" contains 64 bits holding all the information needed
+ * to atomically decide to add, inactivate, enqueue (on an event
+ * queue), dequeue, and/or re-activate workers. To enable this
+ * packing, we restrict maximum parallelism to (1<<15)-1 (which is
+ * far in excess of normal operating range) to allow ids, counts,
+ * and their negations (used for thresholding) to fit into 16bit
+ * fields.
+ *
+ * Field "plock" is a form of sequence lock with a saturating
+ * shutdown bit (similarly for per-queue "qlocks"), mainly
+ * protecting updates to the workQueues array, as well as to
+ * enable shutdown. When used as a lock, it is normally only very
+ * briefly held, so is nearly always available after at most a
+ * brief spin, but we use a monitor-based backup strategy to
+ * block when needed.
*
- * Recording Workers. Workers are recorded in the "workers" array
- * that is created upon pool construction and expanded if (rarely)
- * necessary. This is an array as opposed to some other data
- * structure to support index-based random steals by workers.
- * Updates to the array recording new workers and unrecording
- * terminated ones are protected from each other by a seqLock
- * (scanGuard) but the array is otherwise concurrently readable,
- * and accessed directly by workers. To simplify index-based
- * operations, the array size is always a power of two, and all
- * readers must tolerate null slots. To avoid flailing during
- * start-up, the array is presized to hold twice #parallelism
- * workers (which is unlikely to need further resizing during
- * execution). But to avoid dealing with so many null slots,
- * variable scanGuard includes a mask for the nearest power of two
- * that contains all current workers. All worker thread creation
- * is on-demand, triggered by task submissions, replacement of
- * terminated workers, and/or compensation for blocked
- * workers. However, all other support code is set up to work with
- * other policies. To ensure that we do not hold on to worker
- * references that would prevent GC, ALL accesses to workers are
- * via indices into the workers array (which is one source of some
- * of the messy code constructions here). In essence, the workers
- * array serves as a weak reference mechanism. Thus for example
- * the wait queue field of ctl stores worker indices, not worker
- * references. Access to the workers in associated methods (for
- * example signalWork) must both index-check and null-check the
- * IDs. All such accesses ignore bad IDs by returning out early
- * from what they are doing, since this can only be associated
- * with termination, in which case it is OK to give up.
+ * Recording WorkQueues. WorkQueues are recorded in the
+ * "workQueues" array that is created upon first use and expanded
+ * if necessary. Updates to the array while recording new workers
+ * and unrecording terminated ones are protected from each other
+ * by a lock but the array is otherwise concurrently readable, and
+ * accessed directly. To simplify index-based operations, the
+ * array size is always a power of two, and all readers must
+ * tolerate null slots. Worker queues are at odd indices. Shared
+ * (submission) queues are at even indices, up to a maximum of 64
+ * slots, to limit growth even if array needs to expand to add
+ * more workers. Grouping them together in this way simplifies and
+ * speeds up task scanning.
*
- * All uses of the workers array, as well as queue arrays, check
- * that the array is non-null (even if previously non-null). This
- * allows nulling during termination, which is currently not
- * necessary, but remains an option for resource-revocation-based
- * shutdown schemes.
+ * All worker thread creation is on-demand, triggered by task
+ * submissions, replacement of terminated workers, and/or
+ * compensation for blocked workers. However, all other support
+ * code is set up to work with other policies. To ensure that we
+ * do not hold on to worker references that would prevent GC, ALL
+ * accesses to workQueues are via indices into the workQueues
+ * array (which is one source of some of the messy code
+ * constructions here). In essence, the workQueues array serves as
+ * a weak reference mechanism. Thus for example the wait queue
+ * field of ctl stores indices, not references. Access to the
+ * workQueues in associated methods (for example signalWork) must
+ * both index-check and null-check the IDs. All such accesses
+ * ignore bad IDs by returning out early from what they are doing,
+ * since this can only be associated with termination, in which
+ * case it is OK to give up. All uses of the workQueues array
+ * also check that it is non-null (even if previously
+ * non-null). This allows nulling during termination, which is
+ * currently not necessary, but remains an option for
+ * resource-revocation-based shutdown schemes. It also helps
+ * reduce JIT issuance of uncommon-trap code, which tends to
+ * unnecessarily complicate control flow in some methods.
*
- * Wait Queuing. Unlike HPC work-stealing frameworks, we cannot
+ * Event Queuing. Unlike HPC work-stealing frameworks, we cannot
* let workers spin indefinitely scanning for tasks when none can
* be found immediately, and we cannot start/resume workers unless
* there appear to be tasks available. On the other hand, we must
* quickly prod them into action when new tasks are submitted or
- * generated. We park/unpark workers after placing in an event
- * wait queue when they cannot find work. This "queue" is actually
- * a simple Treiber stack, headed by the "id" field of ctl, plus a
- * 15bit counter value to both wake up waiters (by advancing their
- * count) and avoid ABA effects. Successors are held in worker
- * field "nextWait". Queuing deals with several intrinsic races,
- * mainly that a task-producing thread can miss seeing (and
+ * generated. In many usages, ramp-up time to activate workers is
+ * the main limiting factor in overall performance (this is
+ * compounded at program start-up by JIT compilation and
+ * allocation). So we try to streamline this as much as possible.
+ * We park/unpark workers after placing in an event wait queue
+ * when they cannot find work. This "queue" is actually a simple
+ * Treiber stack, headed by the "id" field of ctl, plus a 15bit
+ * counter value (that reflects the number of times a worker has
+ * been inactivated) to avoid ABA effects (we need only as many
+ * version numbers as worker threads). Successors are held in
+ * field WorkQueue.nextWait. Queuing deals with several intrinsic
+ * races, mainly that a task-producing thread can miss seeing (and
* signalling) another thread that gave up looking for work but
* has not yet entered the wait queue. We solve this by requiring
- * a full sweep of all workers both before (in scan()) and after
- * (in tryAwaitWork()) a newly waiting worker is added to the wait
- * queue. During a rescan, the worker might release some other
- * queued worker rather than itself, which has the same net
- * effect. Because enqueued workers may actually be rescanning
- * rather than waiting, we set and clear the "parked" field of
- * ForkJoinWorkerThread to reduce unnecessary calls to unpark.
- * (Use of the parked field requires a secondary recheck to avoid
- * missed signals.)
+ * a full sweep of all workers (via repeated calls to method
+ * scan()) both before and after a newly waiting worker is added
+ * to the wait queue. During a rescan, the worker might release
+ * some other queued worker rather than itself, which has the same
+ * net effect. Because enqueued workers may actually be rescanning
+ * rather than waiting, we set and clear the "parker" field of
+ * WorkQueues to reduce unnecessary calls to unpark. (This
+ * requires a secondary recheck to avoid missed signals.) Note
+ * the unusual conventions about Thread.interrupts surrounding
+ * parking and other blocking: Because interrupts are used solely
+ * to alert threads to check termination, which is checked anyway
+ * upon blocking, we clear status (using Thread.interrupted)
+ * before any call to park, so that park does not immediately
+ * return due to status being set via some other unrelated call to
+ * interrupt in user code.
*
* Signalling. We create or wake up workers only when there
* appears to be at least one task they might be able to find and
- * execute. When a submission is added or another worker adds a
- * task to a queue that previously had two or fewer tasks, they
- * signal waiting workers (or trigger creation of new ones if
- * fewer than the given parallelism level -- see signalWork).
- * These primary signals are buttressed by signals during rescans
- * as well as those performed when a worker steals a task and
- * notices that there are more tasks too; together these cover the
- * signals needed in cases when more than two tasks are pushed
- * but untaken.
+ * execute. However, many other threads may notice the same task
+ * and each signal to wake up a thread that might take it. So in
+ * general, pools will be over-signalled. When a submission is
+ * added or another worker adds a task to a queue that has fewer
+ * than two tasks, they signal waiting workers (or trigger
+ * creation of new ones if fewer than the given parallelism level
+ * -- signalWork), and may leave a hint to the unparked worker to
+ * help signal others upon wakeup). These primary signals are
+ * buttressed by others (see method helpSignal) whenever other
+ * threads scan for work or do not have a task to process. On
+ * most platforms, signalling (unpark) overhead time is noticeably
+ * long, and the time between signalling a thread and it actually
+ * making progress can be very noticeably long, so it is worth
+ * offloading these delays from critical paths as much as
+ * possible.
*
* Trimming workers. To release resources after periods of lack of
* use, a worker starting to wait when the pool is quiescent will
- * time out and terminate if the pool has remained quiescent for
- * SHRINK_RATE nanosecs. This will slowly propagate, eventually
- * terminating all workers after long periods of non-use.
- *
- * Submissions. External submissions are maintained in an
- * array-based queue that is structured identically to
- * ForkJoinWorkerThread queues except for the use of
- * submissionLock in method addSubmission. Unlike the case for
- * worker queues, multiple external threads can add new
- * submissions, so adding requires a lock.
+ * time out and terminate if the pool has remained quiescent for a
+ * given period -- a short period if there are more threads than
+ * parallelism, longer as the number of threads decreases. This
+ * will slowly propagate, eventually terminating all workers after
+ * periods of non-use.
*
- * Compensation. Beyond work-stealing support and lifecycle
- * control, the main responsibility of this framework is to take
- * actions when one worker is waiting to join a task stolen (or
- * always held by) another. Because we are multiplexing many
- * tasks on to a pool of workers, we can't just let them block (as
- * in Thread.join). We also cannot just reassign the joiner's
- * run-time stack with another and replace it later, which would
- * be a form of "continuation", that even if possible is not
- * necessarily a good idea since we sometimes need both an
- * unblocked task and its continuation to progress. Instead we
- * combine two tactics:
+ * Shutdown and Termination. A call to shutdownNow atomically sets
+ * a plock bit and then (non-atomically) sets each worker's
+ * qlock status, cancels all unprocessed tasks, and wakes up
+ * all waiting workers. Detecting whether termination should
+ * commence after a non-abrupt shutdown() call requires more work
+ * and bookkeeping. We need consensus about quiescence (i.e., that
+ * there is no more work). The active count provides a primary
+ * indication but non-abrupt shutdown still requires a rechecking
+ * scan for any workers that are inactive but not queued.
+ *
+ * Joining Tasks
+ * =============
+ *
+ * Any of several actions may be taken when one worker is waiting
+ * to join a task stolen (or always held) by another. Because we
+ * are multiplexing many tasks on to a pool of workers, we can't
+ * just let them block (as in Thread.join). We also cannot just
+ * reassign the joiner's run-time stack with another and replace
+ * it later, which would be a form of "continuation", that even if
+ * possible is not necessarily a good idea since we sometimes need
+ * both an unblocked task and its continuation to progress.
+ * Instead we combine two tactics:
*
* Helping: Arranging for the joiner to execute some task that it
- * would be running if the steal had not occurred. Method
- * ForkJoinWorkerThread.joinTask tracks joining->stealing
- * links to try to find such a task.
+ * would be running if the steal had not occurred.
*
* Compensating: Unless there are already enough live threads,
- * method tryPreBlock() may create or re-activate a spare
- * thread to compensate for blocked joiners until they
- * unblock.
+ * method tryCompensate() may create or re-activate a spare
+ * thread to compensate for blocked joiners until they unblock.
+ *
+ * A third form (implemented in tryRemoveAndExec) amounts to
+ * helping a hypothetical compensator: If we can readily tell that
+ * a possible action of a compensator is to steal and execute the
+ * task being joined, the joining thread can do so directly,
+ * without the need for a compensation thread (although at the
+ * expense of larger run-time stacks, but the tradeoff is
+ * typically worthwhile).
*
* The ManagedBlocker extension API can't use helping so relies
* only on compensation in method awaitBlocker.
*
+ * The algorithm in tryHelpStealer entails a form of "linear"
+ * helping: Each worker records (in field currentSteal) the most
+ * recent task it stole from some other worker. Plus, it records
+ * (in field currentJoin) the task it is currently actively
+ * joining. Method tryHelpStealer uses these markers to try to
+ * find a worker to help (i.e., steal back a task from and execute
+ * it) that could hasten completion of the actively joined task.
+ * In essence, the joiner executes a task that would be on its own
+ * local deque had the to-be-joined task not been stolen. This may
+ * be seen as a conservative variant of the approach in Wagner &
+ * Calder "Leapfrogging: a portable technique for implementing
+ * efficient futures" SIGPLAN Notices, 1993
+ * (http://portal.acm.org/citation.cfm?id=155354). It differs in
+ * that: (1) We only maintain dependency links across workers upon
+ * steals, rather than use per-task bookkeeping. This sometimes
+ * requires a linear scan of workQueues array to locate stealers,
+ * but often doesn't because stealers leave hints (that may become
+ * stale/wrong) of where to locate them. It is only a hint
+ * because a worker might have had multiple steals and the hint
+ * records only one of them (usually the most current). Hinting
+ * isolates cost to when it is needed, rather than adding to
+ * per-task overhead. (2) It is "shallow", ignoring nesting and
+ * potentially cyclic mutual steals. (3) It is intentionally
+ * racy: field currentJoin is updated only while actively joining,
+ * which means that we miss links in the chain during long-lived
+ * tasks, GC stalls etc (which is OK since blocking in such cases
+ * is usually a good idea). (4) We bound the number of attempts
+ * to find work (see MAX_HELP) and fall back to suspending the
+ * worker and if necessary replacing it with another.
+ *
+ * Helping actions for CountedCompleters are much simpler: Method
+ * helpComplete can take and execute any task with the same root
+ * as the task being waited on. However, this still entails some
+ * traversal of completer chains, so is less efficient than using
+ * CountedCompleters without explicit joins.
+ *
* It is impossible to keep exactly the target parallelism number
* of threads running at any given time. Determining the
* existence of conservatively safe helping targets, the
* availability of already-created spares, and the apparent need
- * to create new spares are all racy and require heuristic
- * guidance, so we rely on multiple retries of each. Currently,
- * in keeping with on-demand signalling policy, we compensate only
- * if blocking would leave less than one active (non-waiting,
- * non-blocked) worker. Additionally, to avoid some false alarms
- * due to GC, lagging counters, system activity, etc, compensated
- * blocking for joins is only attempted after rechecks stabilize
- * (retries are interspersed with Thread.yield, for good
- * citizenship). The variable blockedCount, incremented before
- * blocking and decremented after, is sometimes needed to
- * distinguish cases of waiting for work vs blocking on joins or
- * other managed sync. Both cases are equivalent for most pool
- * control, so we can update non-atomically. (Additionally,
- * contention on blockedCount alleviates some contention on ctl).
+ * to create new spares are all racy, so we rely on multiple
+ * retries of each. Compensation in the apparent absence of
+ * helping opportunities is challenging to control on JVMs, where
+ * GC and other activities can stall progress of tasks that in
+ * turn stall out many other dependent tasks, without us being
+ * able to determine whether they will ever require compensation.
+ * Even though work-stealing otherwise encounters little
+ * degradation in the presence of more threads than cores,
+ * aggressively adding new threads in such cases entails risk of
+ * unwanted positive feedback control loops in which more threads
+ * cause more dependent stalls (as well as delayed progress of
+ * unblocked threads to the point that we know they are available)
+ * leading to more situations requiring more threads, and so
+ * on. This aspect of control can be seen as an (analytically
+ * intractable) game with an opponent that may choose the worst
+ * (for us) active thread to stall at any time. We take several
+ * precautions to bound losses (and thus bound gains), mainly in
+ * methods tryCompensate and awaitJoin.
+ *
+ * Common Pool
+ * ===========
*
- * Shutdown and Termination. A call to shutdownNow atomically sets
- * the ctl stop bit and then (non-atomically) sets each workers
- * "terminate" status, cancels all unprocessed tasks, and wakes up
- * all waiting workers. Detecting whether termination should
- * commence after a non-abrupt shutdown() call requires more work
- * and bookkeeping. We need consensus about quiesence (i.e., that
- * there is no more work) which is reflected in active counts so
- * long as there are no current blockers, as well as possible
- * re-evaluations during independent changes in blocking or
- * quiescing workers.
+ * The static commonPool always exists after static
+ * initialization. Since it (or any other created pool) need
+ * never be used, we minimize initial construction overhead and
+ * footprint to the setup of about a dozen fields, with no nested
+ * allocation. Most bootstrapping occurs within method
+ * fullExternalPush during the first submission to the pool.
*
- * Style notes: There is a lot of representation-level coupling
- * among classes ForkJoinPool, ForkJoinWorkerThread, and
- * ForkJoinTask. Most fields of ForkJoinWorkerThread maintain
- * data structures managed by ForkJoinPool, so are directly
- * accessed. Conversely we allow access to "workers" array by
- * workers, and direct access to ForkJoinTask.status by both
- * ForkJoinPool and ForkJoinWorkerThread. There is little point
+ * When external threads submit to the common pool, they can
+ * perform some subtask processing (see externalHelpJoin and
+ * related methods). We do not need to record whether these
+ * submissions are to the common pool -- if not, externalHelpJoin
+ * returns quickly (at the most helping to signal some common pool
+ * workers). These submitters would otherwise be blocked waiting
+ * for completion, so the extra effort (with liberally sprinkled
+ * task status checks) in inapplicable cases amounts to an odd
+ * form of limited spin-wait before blocking in ForkJoinTask.join.
+ *
+ * Style notes
+ * ===========
+ *
+ * There is a lot of representation-level coupling among classes
+ * ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask. The
+ * fields of WorkQueue maintain data structures managed by
+ * ForkJoinPool, so are directly accessed. There is little point
* trying to reduce this, since any associated future changes in
* representations will need to be accompanied by algorithmic
- * changes anyway. All together, these low-level implementation
- * choices produce as much as a factor of 4 performance
- * improvement compared to naive implementations, and enable the
- * processing of billions of tasks per second, at the expense of
- * some ugliness.
+ * changes anyway. Several methods intrinsically sprawl because
+ * they must accumulate sets of consistent reads of volatiles held
+ * in local variables. Methods signalWork() and scan() are the
+ * main bottlenecks, so are especially heavily
+ * micro-optimized/mangled. There are lots of inline assignments
+ * (of form "while ((local = field) != 0)") which are usually the
+ * simplest way to ensure the required read orderings (which are
+ * sometimes critical). This leads to a "C"-like style of listing
+ * declarations of these locals at the heads of methods or blocks.
+ * There are several occurrences of the unusual "do {} while
+ * (!cas...)" which is the simplest way to force an update of a
+ * CAS'ed variable. There are also other coding oddities (including
+ * several unnecessary-looking hoisted null checks) that help
+ * some methods perform reasonably even when interpreted (not
+ * compiled).
*
- * Methods signalWork() and scan() are the main bottlenecks so are
- * especially heavily micro-optimized/mangled. There are lots of
- * inline assignments (of form "while ((local = field) != 0)")
- * which are usually the simplest way to ensure the required read
- * orderings (which are sometimes critical). This leads to a
- * "C"-like style of listing declarations of these locals at the
- * heads of methods or blocks. There are several occurrences of
- * the unusual "do {} while (!cas...)" which is the simplest way
- * to force an update of a CAS'ed variable. There are also other
- * coding oddities that help some methods perform reasonably even
- * when interpreted (not compiled).
- *
- * The order of declarations in this file is: (1) declarations of
- * statics (2) fields (along with constants used when unpacking
- * some of them), listed in an order that tends to reduce
- * contention among them a bit under most JVMs. (3) internal
- * control methods (4) callbacks and other support for
- * ForkJoinTask and ForkJoinWorkerThread classes, (5) exported
- * methods (plus a few little helpers). (6) static block
- * initializing all statics in a minimally dependent order.
+ * The order of declarations in this file is:
+ * (1) Static utility functions
+ * (2) Nested (static) classes
+ * (3) Static fields
+ * (4) Fields, along with constants used when unpacking some of them
+ * (5) Internal control methods
+ * (6) Callbacks and other support for ForkJoinTask methods
+ * (7) Exported methods
+ * (8) Static block initializing statics in minimally dependent order
*/
+ // Static utilities
+
+ /**
+ * If there is a security manager, makes sure caller has
+ * permission to modify threads.
+ */
+ private static void checkPermission() {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null)
+ security.checkPermission(modifyThreadPermission);
+ }
+
+ // Nested classes
+
/**
* Factory for creating new {@link ForkJoinWorkerThread}s.
* A {@code ForkJoinWorkerThreadFactory} must be defined and used
@@ -384,14 +556,526 @@
* Default ForkJoinWorkerThreadFactory implementation; creates a
* new ForkJoinWorkerThread.
*/
- static class DefaultForkJoinWorkerThreadFactory
+ static final class DefaultForkJoinWorkerThreadFactory
implements ForkJoinWorkerThreadFactory {
- public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
return new ForkJoinWorkerThread(pool);
}
}
/**
+ * Per-thread records for threads that submit to pools. Currently
+ * holds only pseudo-random seed / index that is used to choose
+ * submission queues in method externalPush. In the future, this may
+ * also incorporate a means to implement different task rejection
+ * and resubmission policies.
+ *
+ * Seeds for submitters and workers/workQueues work in basically
+ * the same way but are initialized and updated using slightly
+ * different mechanics. Both are initialized using the same
+ * approach as in class ThreadLocal, where successive values are
+ * unlikely to collide with previous values. Seeds are then
+ * randomly modified upon collisions using xorshifts, which
+ * requires a non-zero seed.
+ */
+ static final class Submitter {
+ int seed;
+ Submitter(int s) { seed = s; }
+ }
+
+ /**
+ * Class for artificial tasks that are used to replace the target
+ * of local joins if they are removed from an interior queue slot
+ * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
+ * actually do anything beyond having a unique identity.
+ */
+ static final class EmptyTask extends ForkJoinTask<Void> {
+ private static final long serialVersionUID = -7721805057305804111L;
+ EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void x) {}
+ public final boolean exec() { return true; }
+ }
+
+ /**
+ * Queues supporting work-stealing as well as external task
+ * submission. See above for main rationale and algorithms.
+ * Implementation relies heavily on "Unsafe" intrinsics
+ * and selective use of "volatile":
+ *
+ * Field "base" is the index (mod array.length) of the least valid
+ * queue slot, which is always the next position to steal (poll)
+ * from if nonempty. Reads and writes require volatile orderings
+ * but not CAS, because updates are only performed after slot
+ * CASes.
+ *
+ * Field "top" is the index (mod array.length) of the next queue
+ * slot to push to or pop from. It is written only by owner thread
+ * for push, or under lock for external/shared push, and accessed
+ * by other threads only after reading (volatile) base. Both top
+ * and base are allowed to wrap around on overflow, but (top -
+ * base) (or more commonly -(base - top) to force volatile read of
+ * base before top) still estimates size. The lock ("qlock") is
+ * forced to -1 on termination, causing all further lock attempts
+ * to fail. (Note: we don't need CAS for termination state because
+ * upon pool shutdown, all shared-queues will stop being used
+ * anyway.) Nearly all lock bodies are set up so that exceptions
+ * within lock bodies are "impossible" (modulo JVM errors that
+ * would cause failure anyway.)
+ *
+ * The array slots are read and written using the emulation of
+ * volatiles/atomics provided by Unsafe. Insertions must in
+ * general use putOrderedObject as a form of releasing store to
+ * ensure that all writes to the task object are ordered before
+ * its publication in the queue. All removals entail a CAS to
+ * null. The array is always a power of two. To ensure safety of
+ * Unsafe array operations, all accesses perform explicit null
+ * checks and implicit bounds checks via power-of-two masking.
+ *
+ * In addition to basic queuing support, this class contains
+ * fields described elsewhere to control execution. It turns out
+ * to work better memory-layout-wise to include them in this class
+ * rather than a separate class.
+ *
+ * Performance on most platforms is very sensitive to placement of
+ * instances of both WorkQueues and their arrays -- we absolutely
+ * do not want multiple WorkQueue instances or multiple queue
+ * arrays sharing cache lines. (It would be best for queue objects
+ * and their arrays to share, but there is nothing available to
+ * help arrange that). Unfortunately, because they are recorded
+ * in a common array, WorkQueue instances are often moved to be
+ * adjacent by garbage collectors. To reduce impact, we use field
+ * padding that works OK on common platforms; this effectively
+ * trades off slightly slower average field access for the sake of
+ * avoiding really bad worst-case access. (Until better JVM
+ * support is in place, this padding is dependent on transient
+ * properties of JVM field layout rules.) We also take care in
+ * allocating, sizing and resizing the array. Non-shared queue
+ * arrays are initialized by workers before use. Others are
+ * allocated on first use.
+ */
+ static final class WorkQueue {
+ /**
+ * Capacity of work-stealing queue array upon initialization.
+ * Must be a power of two; at least 4, but should be larger to
+ * reduce or eliminate cacheline sharing among queues.
+ * Currently, it is much larger, as a partial workaround for
+ * the fact that JVMs often place arrays in locations that
+ * share GC bookkeeping (especially cardmarks) such that
+ * per-write accesses encounter serious memory contention.
+ */
+ static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
+
+ /**
+ * Maximum size for queue arrays. Must be a power of two less
+ * than or equal to 1 << (31 - width of array entry) to ensure
+ * lack of wraparound of index calculations, but defined to a
+ * value a bit less than this to help users trap runaway
+ * programs before saturating systems.
+ */
+ static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
+
+ // Heuristic padding to ameliorate unfortunate memory placements
+ volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+
+ int seed; // for random scanning; initialize nonzero
+ volatile int eventCount; // encoded inactivation count; < 0 if inactive
+ int nextWait; // encoded record of next event waiter
+ int hint; // steal or signal hint (index)
+ int poolIndex; // index of this queue in pool (or 0)
+ final int mode; // 0: lifo, > 0: fifo, < 0: shared
+ int nsteals; // number of steals
+ volatile int qlock; // 1: locked, -1: terminate; else 0
+ volatile int base; // index of next slot for poll
+ int top; // index of next slot for push
+ ForkJoinTask<?>[] array; // the elements (initially unallocated)
+ final ForkJoinPool pool; // the containing pool (may be null)
+ final ForkJoinWorkerThread owner; // owning thread or null if shared
+ volatile Thread parker; // == owner during call to park; else null
+ volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
+ ForkJoinTask<?> currentSteal; // current non-local task being executed
+
+ volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+ volatile Object pad18, pad19, pad1a, pad1b, pad1c, pad1d;
+
+ WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner, int mode,
+ int seed) {
+ this.pool = pool;
+ this.owner = owner;
+ this.mode = mode;
+ this.seed = seed;
+ // Place indices in the center of array (that is not yet allocated)
+ base = top = INITIAL_QUEUE_CAPACITY >>> 1;
+ }
+
+ /**
+ * Returns the approximate number of tasks in the queue.
+ */
+ final int queueSize() {
+ int n = base - top; // non-owner callers must read base first
+ return (n >= 0) ? 0 : -n; // ignore transient negative
+ }
+
+ /**
+ * Provides a more accurate estimate of whether this queue has
+ * any tasks than does queueSize, by checking whether a
+ * near-empty queue has at least one unclaimed task.
+ */
+ final boolean isEmpty() {
+ ForkJoinTask<?>[] a; int m, s;
+ int n = base - (s = top);
+ return (n >= 0 ||
+ (n == -1 &&
+ ((a = array) == null ||
+ (m = a.length - 1) < 0 ||
+ U.getObject
+ (a, (long)((m & (s - 1)) << ASHIFT) + ABASE) == null)));
+ }
+
+ /**
+ * Pushes a task. Call only by owner in unshared queues. (The
+ * shared-queue version is embedded in method externalPush.)
+ *
+ * @param task the task. Caller must ensure non-null.
+ * @throw RejectedExecutionException if array cannot be resized
+ */
+ final void push(ForkJoinTask<?> task) {
+ ForkJoinTask<?>[] a; ForkJoinPool p;
+ int s = top, m, n;
+ if ((a = array) != null) { // ignore if queue removed
+ int j = (((m = a.length - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ if ((n = (top = s + 1) - base) <= 2) {
+ if ((p = pool) != null)
+ p.signalWork(this);
+ }
+ else if (n >= m)
+ growArray();
+ }
+ }
+
+ /**
+ * Initializes or doubles the capacity of array. Call either
+ * by owner or with lock held -- it is OK for base, but not
+ * top, to move while resizings are in progress.
+ */
+ final ForkJoinTask<?>[] growArray() {
+ ForkJoinTask<?>[] oldA = array;
+ int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
+ if (size > MAXIMUM_QUEUE_CAPACITY)
+ throw new RejectedExecutionException("Queue capacity exceeded");
+ int oldMask, t, b;
+ ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
+ if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
+ (t = top) - (b = base) > 0) {
+ int mask = size - 1;
+ do {
+ ForkJoinTask<?> x;
+ int oldj = ((b & oldMask) << ASHIFT) + ABASE;
+ int j = ((b & mask) << ASHIFT) + ABASE;
+ x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
+ if (x != null &&
+ U.compareAndSwapObject(oldA, oldj, x, null))
+ U.putObjectVolatile(a, j, x);
+ } while (++b != t);
+ }
+ return a;
+ }
+
+ /**
+ * Takes next task, if one exists, in LIFO order. Call only
+ * by owner in unshared queues.
+ */
+ final ForkJoinTask<?> pop() {
+ ForkJoinTask<?>[] a; ForkJoinTask<?> t; int m;
+ if ((a = array) != null && (m = a.length - 1) >= 0) {
+ for (int s; (s = top - 1) - base >= 0;) {
+ long j = ((m & s) << ASHIFT) + ABASE;
+ if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null)
+ break;
+ if (U.compareAndSwapObject(a, j, t, null)) {
+ top = s;
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes a task in FIFO order if b is base of queue and a task
+ * can be claimed without contention. Specialized versions
+ * appear in ForkJoinPool methods scan and tryHelpStealer.
+ */
+ final ForkJoinTask<?> pollAt(int b) {
+ ForkJoinTask<?> t; ForkJoinTask<?>[] a;
+ if ((a = array) != null) {
+ int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) != null &&
+ base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes next task, if one exists, in FIFO order.
+ */
+ final ForkJoinTask<?> poll() {
+ ForkJoinTask<?>[] a; int b; ForkJoinTask<?> t;
+ while ((b = base) - top < 0 && (a = array) != null) {
+ int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ if (t != null) {
+ if (base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ return t;
+ }
+ }
+ else if (base == b) {
+ if (b + 1 == top)
+ break;
+ Thread.yield(); // wait for lagging update (very rare)
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Takes next task, if one exists, in order specified by mode.
+ */
+ final ForkJoinTask<?> nextLocalTask() {
+ return mode == 0 ? pop() : poll();
+ }
+
+ /**
+ * Returns next task, if one exists, in order specified by mode.
+ */
+ final ForkJoinTask<?> peek() {
+ ForkJoinTask<?>[] a = array; int m;
+ if (a == null || (m = a.length - 1) < 0)
+ return null;
+ int i = mode == 0 ? top - 1 : base;
+ int j = ((i & m) << ASHIFT) + ABASE;
+ return (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ }
+
+ /**
+ * Pops the given task only if it is at the current top.
+ * (A shared version is available only via FJP.tryExternalUnpush)
+ */
+ final boolean tryUnpush(ForkJoinTask<?> t) {
+ ForkJoinTask<?>[] a; int s;
+ if ((a = array) != null && (s = top) != base &&
+ U.compareAndSwapObject
+ (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
+ top = s;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes and cancels all known tasks, ignoring any exceptions.
+ */
+ final void cancelAll() {
+ ForkJoinTask.cancelIgnoringExceptions(currentJoin);
+ ForkJoinTask.cancelIgnoringExceptions(currentSteal);
+ for (ForkJoinTask<?> t; (t = poll()) != null; )
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+
+ /**
+ * Computes next value for random probes. Scans don't require
+ * a very high quality generator, but also not a crummy one.
+ * Marsaglia xor-shift is cheap and works well enough. Note:
+ * This is manually inlined in its usages in ForkJoinPool to
+ * avoid writes inside busy scan loops.
+ */
+ final int nextSeed() {
+ int r = seed;
+ r ^= r << 13;
+ r ^= r >>> 17;
+ return seed = r ^= r << 5;
+ }
+
+ // Specialized execution methods
+
+ /**
+ * Pops and runs tasks until empty.
+ */
+ private void popAndExecAll() {
+ // A bit faster than repeated pop calls
+ ForkJoinTask<?>[] a; int m, s; long j; ForkJoinTask<?> t;
+ while ((a = array) != null && (m = a.length - 1) >= 0 &&
+ (s = top - 1) - base >= 0 &&
+ (t = ((ForkJoinTask<?>)
+ U.getObject(a, j = ((m & s) << ASHIFT) + ABASE)))
+ != null) {
+ if (U.compareAndSwapObject(a, j, t, null)) {
+ top = s;
+ t.doExec();
+ }
+ }
+ }
+
+ /**
+ * Polls and runs tasks until empty.
+ */
+ private void pollAndExecAll() {
+ for (ForkJoinTask<?> t; (t = poll()) != null;)
+ t.doExec();
+ }
+
+ /**
+ * If present, removes from queue and executes the given task,
+ * or any other cancelled task. Returns (true) on any CAS
+ * or consistency check failure so caller can retry.
+ *
+ * @return false if no progress can be made, else true;
+ */
+ final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
+ boolean stat = true, removed = false, empty = true;
+ ForkJoinTask<?>[] a; int m, s, b, n;
+ if ((a = array) != null && (m = a.length - 1) >= 0 &&
+ (n = (s = top) - (b = base)) > 0) {
+ for (ForkJoinTask<?> t;;) { // traverse from s to b
+ int j = ((--s & m) << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ if (t == null) // inconsistent length
+ break;
+ else if (t == task) {
+ if (s + 1 == top) { // pop
+ if (!U.compareAndSwapObject(a, j, task, null))
+ break;
+ top = s;
+ removed = true;
+ }
+ else if (base == b) // replace with proxy
+ removed = U.compareAndSwapObject(a, j, task,
+ new EmptyTask());
+ break;
+ }
+ else if (t.status >= 0)
+ empty = false;
+ else if (s + 1 == top) { // pop and throw away
+ if (U.compareAndSwapObject(a, j, t, null))
+ top = s;
+ break;
+ }
+ if (--n == 0) {
+ if (!empty && base == b)
+ stat = false;
+ break;
+ }
+ }
+ }
+ if (removed)
+ task.doExec();
+ return stat;
+ }
+
+ /**
+ * Polls for and executes the given task or any other task in
+ * its CountedCompleter computation
+ */
+ final boolean pollAndExecCC(ForkJoinTask<?> root) {
+ ForkJoinTask<?>[] a; int b; Object o;
+ outer: while ((b = base) - top < 0 && (a = array) != null) {
+ long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ if ((o = U.getObject(a, j)) == null ||
+ !(o instanceof CountedCompleter))
+ break;
+ for (CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;;) {
+ if (r == root) {
+ if (base == b &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ base = b + 1;
+ t.doExec();
+ return true;
+ }
+ else
+ break; // restart
+ }
+ if ((r = r.completer) == null)
+ break outer; // not part of root computation
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Executes a top-level task and any local tasks remaining
+ * after execution.
+ */
+ final void runTask(ForkJoinTask<?> t) {
+ if (t != null) {
+ (currentSteal = t).doExec();
+ currentSteal = null;
+ ++nsteals;
+ if (base - top < 0) { // process remaining local tasks
+ if (mode == 0)
+ popAndExecAll();
+ else
+ pollAndExecAll();
+ }
+ }
+ }
+
+ /**
+ * Executes a non-top-level (stolen) task.
+ */
+ final void runSubtask(ForkJoinTask<?> t) {
+ if (t != null) {
+ ForkJoinTask<?> ps = currentSteal;
+ (currentSteal = t).doExec();
+ currentSteal = ps;
+ }
+ }
+
+ /**
+ * Returns true if owned and not known to be blocked.
+ */
+ final boolean isApparentlyUnblocked() {
+ Thread wt; Thread.State s;
+ return (eventCount >= 0 &&
+ (wt = owner) != null &&
+ (s = wt.getState()) != Thread.State.BLOCKED &&
+ s != Thread.State.WAITING &&
+ s != Thread.State.TIMED_WAITING);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U;
+ private static final long QLOCK;
+ private static final int ABASE;
+ private static final int ASHIFT;
+ static {
+ int s;
+ try {
+ U = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = WorkQueue.class;
+ Class<?> ak = ForkJoinTask[].class;
+ QLOCK = U.objectFieldOffset
+ (k.getDeclaredField("qlock"));
+ ABASE = U.arrayBaseOffset(ak);
+ s = U.arrayIndexScale(ak);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ if ((s & (s-1)) != 0)
+ throw new Error("data type scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+ }
+ }
+
+ // static fields (initialized in static initializer below)
+
+ /**
* Creates a new ForkJoinWorkerThread. This factory is used unless
* overridden in ForkJoinPool constructors.
*/
@@ -399,107 +1083,93 @@
defaultForkJoinWorkerThreadFactory;
/**
+ * Per-thread submission bookkeeping. Shared across all pools
+ * to reduce ThreadLocal pollution and because random motion
+ * to avoid contention in one pool is likely to hold for others.
+ * Lazily initialized on first submission (but null-checked
+ * in other contexts to avoid unnecessary initialization).
+ */
+ static final ThreadLocal<Submitter> submitters;
+
+ /**
* Permission required for callers of methods that may start or
* kill threads.
*/
private static final RuntimePermission modifyThreadPermission;
/**
- * If there is a security manager, makes sure caller has
- * permission to modify threads.
+ * Common (static) pool. Non-null for public use unless a static
+ * construction exception, but internal usages null-check on use
+ * to paranoically avoid potential initialization circularities
+ * as well as to simplify generated code.
*/
- private static void checkPermission() {
- SecurityManager security = System.getSecurityManager();
- if (security != null)
- security.checkPermission(modifyThreadPermission);
- }
+ static final ForkJoinPool commonPool;
/**
- * Generator for assigning sequence numbers as pool names.
+ * Common pool parallelism. Must equal commonPool.parallelism.
*/
- private static final AtomicInteger poolNumberGenerator;
+ static final int commonPoolParallelism;
/**
- * Generator for initial random seeds for worker victim
- * selection. This is used only to create initial seeds. Random
- * steals use a cheaper xorshift generator per steal attempt. We
- * don't expect much contention on seedGenerator, so just use a
- * plain Random.
+ * Sequence number for creating workerNamePrefix.
*/
- static final Random workerSeedGenerator;
+ private static int poolNumberSequence;
/**
- * Array holding all worker threads in the pool. Initialized upon
- * construction. Array size must be a power of two. Updates and
- * replacements are protected by scanGuard, but the array is
- * always kept in a consistent enough state to be randomly
- * accessed without locking by workers performing work-stealing,
- * as well as other traversal-based methods in this class, so long
- * as reads memory-acquire by first reading ctl. All readers must
- * tolerate that some array slots may be null.
+ * Return the next sequence number. We don't expect this to
+ * ever contend so use simple builtin sync.
*/
- ForkJoinWorkerThread[] workers;
+ private static final synchronized int nextPoolId() {
+ return ++poolNumberSequence;
+ }
- /**
- * Initial size for submission queue array. Must be a power of
- * two. In many applications, these always stay small so we use a
- * small initial cap.
- */
- private static final int INITIAL_QUEUE_CAPACITY = 8;
+ // static constants
/**
- * Maximum size for submission queue array. Must be a power of two
- * less than or equal to 1 << (31 - width of array entry) to
- * ensure lack of index wraparound, but is capped at a lower
- * value to help users trap runaway computations.
+ * Initial timeout value (in nanoseconds) for the thread
+ * triggering quiescence to park waiting for new work. On timeout,
+ * the thread will instead try to shrink the number of
+ * workers. The value should be large enough to avoid overly
+ * aggressive shrinkage during most transient stalls (long GCs
+ * etc).
*/
- private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
+ private static final long IDLE_TIMEOUT = 2000L * 1000L * 1000L; // 2sec
/**
- * Array serving as submission queue. Initialized upon construction.
+ * Timeout value when there are more threads than parallelism level
*/
- private ForkJoinTask<?>[] submissionQueue;
+ private static final long FAST_IDLE_TIMEOUT = 200L * 1000L * 1000L;
/**
- * Lock protecting submissions array for addSubmission
+ * Tolerance for idle timeouts, to cope with timer undershoots
*/
- private final ReentrantLock submissionLock;
-
- /**
- * Condition for awaitTermination, using submissionLock for
- * convenience.
- */
- private final Condition termination;
+ private static final long TIMEOUT_SLOP = 2000000L;
/**
- * Creation factory for worker threads.
+ * The maximum stolen->joining link depth allowed in method
+ * tryHelpStealer. Must be a power of two. Depths for legitimate
+ * chains are unbounded, but we use a fixed constant to avoid
+ * (otherwise unchecked) cycles and to bound staleness of
+ * traversal parameters at the expense of sometimes blocking when
+ * we could be helping.
*/
- private final ForkJoinWorkerThreadFactory factory;
-
- /**
- * The uncaught exception handler used when any worker abruptly
- * terminates.
- */
- final Thread.UncaughtExceptionHandler ueh;
+ private static final int MAX_HELP = 64;
/**
- * Prefix for assigning names to worker threads
+ * Increment for seed generators. See class ThreadLocal for
+ * explanation.
*/
- private final String workerNamePrefix;
+ private static final int SEED_INCREMENT = 0x61c88647;
/**
- * Sum of per-thread steal counts, updated only when threads are
- * idle or terminating.
- */
- private volatile long stealCount;
-
- /**
- * Main pool control -- a long packed with:
+ * Bits and masks for control variables
+ *
+ * Field ctl is a long packed with:
* AC: Number of active running workers minus target parallelism (16 bits)
- * TC: Number of total workers minus target parallelism (16bits)
+ * TC: Number of total workers minus target parallelism (16 bits)
* ST: true if pool is terminating (1 bit)
* EC: the wait count of top waiting thread (15 bits)
- * ID: ~poolIndex of top of Treiber stack of waiting threads (16 bits)
+ * ID: poolIndex of top of Treiber stack of waiters (16 bits)
*
* When convenient, we can extract the upper 32 bits of counts and
* the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
@@ -508,13 +1178,26 @@
* parallelism and the positionings of fields makes it possible to
* perform the most common checks via sign tests of fields: When
* ac is negative, there are not enough active workers, when tc is
- * negative, there are not enough total workers, when id is
- * negative, there is at least one waiting worker, and when e is
+ * negative, there are not enough total workers, and when e is
* negative, the pool is terminating. To deal with these possibly
* negative fields, we use casts in and out of "short" and/or
* signed shifts to maintain signedness.
+ *
+ * When a thread is queued (inactivated), its eventCount field is
+ * set negative, which is the only way to tell if a worker is
+ * prevented from executing tasks, even though it must continue to
+ * scan for them to avoid queuing races. Note however that
+ * eventCount updates lag releases so usage requires care.
+ *
+ * Field plock is an int packed with:
+ * SHUTDOWN: true if shutdown is enabled (1 bit)
+ * SEQ: a sequence lock, with PL_LOCK bit set if locked (30 bits)
+ * SIGNAL: set when threads may be waiting on the lock (1 bit)
+ *
+ * The sequence number enables simple consistency checks:
+ * Staleness of read-only operations on the workQueues array can
+ * be checked by comparing plock before vs after the reads.
*/
- volatile long ctl;
// bit positions/shifts for fields
private static final int AC_SHIFT = 48;
@@ -523,8 +1206,10 @@
private static final int EC_SHIFT = 16;
// bounds
- private static final int MAX_ID = 0x7fff; // max poolIndex
- private static final int SMASK = 0xffff; // mask short bits
+ private static final int SMASK = 0xffff; // short bits
+ private static final int MAX_CAP = 0x7fff; // max #workers - 1
+ private static final int EVENMASK = 0xfffe; // even short bits
+ private static final int SQMASK = 0x007e; // max 64 (even) slots
private static final int SHORT_SIGN = 1 << 15;
private static final int INT_SIGN = 1 << 31;
@@ -546,649 +1231,648 @@
private static final int UTC_UNIT = 1 << UTC_SHIFT;
// masks and units for dealing with e = (int)ctl
- private static final int E_MASK = 0x7fffffff; // no STOP_BIT
- private static final int EC_UNIT = 1 << EC_SHIFT;
-
- /**
- * The target parallelism level.
- */
- final int parallelism;
+ private static final int E_MASK = 0x7fffffff; // no STOP_BIT
+ private static final int E_SEQ = 1 << EC_SHIFT;
- /**
- * Index (mod submission queue length) of next element to take
- * from submission queue. Usage is identical to that for
- * per-worker queues -- see ForkJoinWorkerThread internal
- * documentation.
- */
- volatile int queueBase;
-
- /**
- * Index (mod submission queue length) of next element to add
- * in submission queue. Usage is identical to that for
- * per-worker queues -- see ForkJoinWorkerThread internal
- * documentation.
- */
- int queueTop;
+ // plock bits
+ private static final int SHUTDOWN = 1 << 31;
+ private static final int PL_LOCK = 2;
+ private static final int PL_SIGNAL = 1;
+ private static final int PL_SPINS = 1 << 8;
- /**
- * True when shutdown() has been called.
- */
- volatile boolean shutdown;
-
- /**
- * True if use local fifo, not default lifo, for local polling
- * Read by, and replicated by ForkJoinWorkerThreads
- */
- final boolean locallyFifo;
+ // access mode for WorkQueue
+ static final int LIFO_QUEUE = 0;
+ static final int FIFO_QUEUE = 1;
+ static final int SHARED_QUEUE = -1;
- /**
- * The number of threads in ForkJoinWorkerThreads.helpQuiescePool.
- * When non-zero, suppresses automatic shutdown when active
- * counts become zero.
- */
- volatile int quiescerCount;
+ // bounds for #steps in scan loop -- must be power 2 minus 1
+ private static final int MIN_SCAN = 0x1ff; // cover estimation slop
+ private static final int MAX_SCAN = 0x1ffff; // 4 * max workers
+
+ // Instance fields
- /**
- * The number of threads blocked in join.
- */
- volatile int blockedCount;
-
- /**
- * Counter for worker Thread names (unrelated to their poolIndex)
+ /*
+ * Field layout of this class tends to matter more than one would
+ * like. Runtime layout order is only loosely related to
+ * declaration order and may differ across JVMs, but the following
+ * empirically works OK on current JVMs.
*/
- private volatile int nextWorkerNumber;
- /**
- * The index for the next created worker. Accessed under scanGuard.
- */
- private int nextWorkerIndex;
+ // Heuristic padding to ameliorate unfortunate memory placements
+ volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
- /**
- * SeqLock and index masking for updates to workers array. Locked
- * when SG_UNIT is set. Unlocking clears bit by adding
- * SG_UNIT. Staleness of read-only operations can be checked by
- * comparing scanGuard to value before the reads. The low 16 bits
- * (i.e, anding with SMASK) hold (the smallest power of two
- * covering all worker indices, minus one, and is used to avoid
- * dealing with large numbers of null slots when the workers array
- * is overallocated.
- */
- volatile int scanGuard;
+ volatile long stealCount; // collects worker counts
+ volatile long ctl; // main pool control
+ volatile int plock; // shutdown status and seqLock
+ volatile int indexSeed; // worker/submitter index seed
+ final int config; // mode and parallelism level
+ WorkQueue[] workQueues; // main registry
+ final ForkJoinWorkerThreadFactory factory;
+ final Thread.UncaughtExceptionHandler ueh; // per-worker UEH
+ final String workerNamePrefix; // to create worker name string
- private static final int SG_UNIT = 1 << 16;
+ volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
+ volatile Object pad18, pad19, pad1a, pad1b;
- /**
- * The wakeup interval (in nanoseconds) for a worker waiting for a
- * task when the pool is quiescent to instead try to shrink the
- * number of workers. The exact value does not matter too
- * much. It must be short enough to release resources during
- * sustained periods of idleness, but not so short that threads
- * are continually re-created.
+ /*
+ * Acquires the plock lock to protect worker array and related
+ * updates. This method is called only if an initial CAS on plock
+ * fails. This acts as a spinLock for normal cases, but falls back
+ * to builtin monitor to block when (rarely) needed. This would be
+ * a terrible idea for a highly contended lock, but works fine as
+ * a more conservative alternative to a pure spinlock.
*/
- private static final long SHRINK_RATE =
- 4L * 1000L * 1000L * 1000L; // 4 seconds
-
- /**
- * Top-level loop for worker threads: On each step: if the
- * previous step swept through all queues and found no tasks, or
- * there are excess threads, then possibly blocks. Otherwise,
- * scans for and, if found, executes a task. Returns when pool
- * and/or worker terminate.
- *
- * @param w the worker
- */
- final void work(ForkJoinWorkerThread w) {
- boolean swept = false; // true on empty scans
- long c;
- while (!w.terminate && (int)(c = ctl) >= 0) {
- int a; // active count
- if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0)
- swept = scan(w, a);
- else if (tryAwaitWork(w, c))
- swept = false;
+ private int acquirePlock() {
+ int spins = PL_SPINS, r = 0, ps, nps;
+ for (;;) {
+ if (((ps = plock) & PL_LOCK) == 0 &&
+ U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
+ return nps;
+ else if (r == 0) { // randomize spins if possible
+ Thread t = Thread.currentThread(); WorkQueue w; Submitter z;
+ if ((t instanceof ForkJoinWorkerThread) &&
+ (w = ((ForkJoinWorkerThread)t).workQueue) != null)
+ r = w.seed;
+ else if ((z = submitters.get()) != null)
+ r = z.seed;
+ else
+ r = 1;
+ }
+ else if (spins >= 0) {
+ r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
+ if (r >= 0)
+ --spins;
+ }
+ else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
+ synchronized (this) {
+ if ((plock & PL_SIGNAL) != 0) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ try {
+ Thread.currentThread().interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ }
+ else
+ notifyAll();
+ }
+ }
}
}
- // Signalling
+ /**
+ * Unlocks and signals any thread waiting for plock. Called only
+ * when CAS of seq value for unlock fails.
+ */
+ private void releasePlock(int ps) {
+ plock = ps;
+ synchronized (this) { notifyAll(); }
+ }
/**
- * Wakes up or creates a worker.
+ * Performs secondary initialization, called when plock is zero.
+ * Creates workQueue array and sets plock to a valid value. The
+ * lock body must be exception-free (so no try/finally) so we
+ * optimistically allocate new array outside the lock and throw
+ * away if (very rarely) not needed. (A similar tactic is used in
+ * fullExternalPush.) Because the plock seq value can eventually
+ * wrap around zero, this method harmlessly fails to reinitialize
+ * if workQueues exists, while still advancing plock.
+ *
+ * Additionally tries to create the first worker.
*/
- final void signalWork() {
- /*
- * The while condition is true if: (there is are too few total
- * workers OR there is at least one waiter) AND (there are too
- * few active workers OR the pool is terminating). The value
- * of e distinguishes the remaining cases: zero (no waiters)
- * for create, negative if terminating (in which case do
- * nothing), else release a waiter. The secondary checks for
- * release (non-null array etc) can fail if the pool begins
- * terminating after the test, and don't impose any added cost
- * because JVMs must perform null and bounds checks anyway.
- */
- long c; int e, u;
- while ((((e = (int)(c = ctl)) | (u = (int)(c >>> 32))) &
- (INT_SIGN|SHORT_SIGN)) == (INT_SIGN|SHORT_SIGN) && e >= 0) {
- if (e > 0) { // release a waiting worker
- int i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
- if ((ws = workers) == null ||
- (i = ~e & SMASK) >= ws.length ||
- (w = ws[i]) == null)
- break;
- long nc = (((long)(w.nextWait & E_MASK)) |
- ((long)(u + UAC_UNIT) << 32));
- if (w.eventCount == e &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
- break;
+ private void initWorkers() {
+ WorkQueue[] ws, nws; int ps;
+ int p = config & SMASK; // find power of two table size
+ int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
+ n = (n + 1) << 1;
+ if ((ws = workQueues) == null || ws.length == 0)
+ nws = new WorkQueue[n];
+ else
+ nws = null;
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if (((ws = workQueues) == null || ws.length == 0) && nws != null)
+ workQueues = nws;
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ tryAddWorker();
+ }
+
+ /**
+ * Tries to create and start one worker if fewer than target
+ * parallelism level exist. Adjusts counts etc on failure.
+ */
+ private void tryAddWorker() {
+ long c; int u;
+ while ((u = (int)((c = ctl) >>> 32)) < 0 &&
+ (u & SHORT_SIGN) != 0 && (int)c == 0) {
+ long nc = (long)(((u + UTC_UNIT) & UTC_MASK) |
+ ((u + UAC_UNIT) & UAC_MASK)) << 32;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ ForkJoinWorkerThreadFactory fac;
+ Throwable ex = null;
+ ForkJoinWorkerThread wt = null;
+ try {
+ if ((fac = factory) != null &&
+ (wt = fac.newThread(this)) != null) {
+ wt.start();
+ break;
+ }
+ } catch (Throwable e) {
+ ex = e;
}
- }
- else if (UNSAFE.compareAndSwapLong
- (this, ctlOffset, c,
- (long)(((u + UTC_UNIT) & UTC_MASK) |
- ((u + UAC_UNIT) & UAC_MASK)) << 32)) {
- addWorker();
+ deregisterWorker(wt, ex);
break;
}
}
}
+ // Registering and deregistering workers
+
/**
- * Variant of signalWork to help release waiters on rescans.
- * Tries once to release a waiter if active count < 0.
+ * Callback from ForkJoinWorkerThread to establish and record its
+ * WorkQueue. To avoid scanning bias due to packing entries in
+ * front of the workQueues array, we treat the array as a simple
+ * power-of-two hash table using per-thread seed as hash,
+ * expanding as needed.
+ *
+ * @param wt the worker thread
+ * @return the worker's queue
+ */
+ final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
+ Thread.UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
+ wt.setDaemon(true);
+ if ((handler = ueh) != null)
+ wt.setUncaughtExceptionHandler(handler);
+ do {} while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed,
+ s += SEED_INCREMENT) ||
+ s == 0); // skip 0
+ WorkQueue w = new WorkQueue(this, wt, config >>> 16, s);
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ try {
+ if ((ws = workQueues) != null) { // skip if shutting down
+ int n = ws.length, m = n - 1;
+ int r = (s << 1) | 1; // use odd-numbered indices
+ if (ws[r &= m] != null) { // collision
+ int probes = 0; // step by approx half size
+ int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
+ while (ws[r = (r + step) & m] != null) {
+ if (++probes >= n) {
+ workQueues = ws = Arrays.copyOf(ws, n <<= 1);
+ m = n - 1;
+ probes = 0;
+ }
+ }
+ }
+ w.eventCount = w.poolIndex = r; // volatile write orders
+ ws[r] = w;
+ }
+ } finally {
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex)));
+ return w;
+ }
+
+ /**
+ * Final callback from terminating worker, as well as upon failure
+ * to construct or start a worker. Removes record of worker from
+ * array, and adjusts counts. If pool is shutting down, tries to
+ * complete termination.
*
- * @return false if failed due to contention, else true
+ * @param wt the worker thread or null if construction failed
+ * @param ex the exception causing failure, or null if none
+ */
+ final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
+ WorkQueue w = null;
+ if (wt != null && (w = wt.workQueue) != null) {
+ int ps;
+ w.qlock = -1; // ensure set
+ long ns = w.nsteals, sc; // collect steal count
+ do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
+ sc = stealCount, sc + ns));
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ try {
+ int idx = w.poolIndex;
+ WorkQueue[] ws = workQueues;
+ if (ws != null && idx >= 0 && idx < ws.length && ws[idx] == w)
+ ws[idx] = null;
+ } finally {
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ }
+
+ long c; // adjust ctl counts
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, (((c - AC_UNIT) & AC_MASK) |
+ ((c - TC_UNIT) & TC_MASK) |
+ (c & ~(AC_MASK|TC_MASK)))));
+
+ if (!tryTerminate(false, false) && w != null && w.array != null) {
+ w.cancelAll(); // cancel remaining tasks
+ WorkQueue[] ws; WorkQueue v; Thread p; int u, i, e;
+ while ((u = (int)((c = ctl) >>> 32)) < 0 && (e = (int)c) >= 0) {
+ if (e > 0) { // activate or create replacement
+ if ((ws = workQueues) == null ||
+ (i = e & SMASK) >= ws.length ||
+ (v = ws[i]) != null)
+ break;
+ long nc = (((long)(v.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (v.eventCount != (e | INT_SIGN))
+ break;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = v.parker) != null)
+ U.unpark(p);
+ break;
+ }
+ }
+ else {
+ if ((short)u < 0)
+ tryAddWorker();
+ break;
+ }
+ }
+ }
+ if (ex == null) // help clean refs on way out
+ ForkJoinTask.helpExpungeStaleExceptions();
+ else // rethrow
+ ForkJoinTask.rethrow(ex);
+ }
+
+ // Submissions
+
+ /**
+ * Unless shutting down, adds the given task to a submission queue
+ * at submitter's current queue index (modulo submission
+ * range). Only the most common path is directly handled in this
+ * method. All others are relayed to fullExternalPush.
+ *
+ * @param task the task. Caller must ensure non-null.
*/
- private boolean tryReleaseWaiter() {
- long c; int e, i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws;
- if ((e = (int)(c = ctl)) > 0 &&
- (int)(c >> AC_SHIFT) < 0 &&
- (ws = workers) != null &&
- (i = ~e & SMASK) < ws.length &&
- (w = ws[i]) != null) {
- long nc = ((long)(w.nextWait & E_MASK) |
- ((c + AC_UNIT) & (AC_MASK|TC_MASK)));
- if (w.eventCount != e ||
- !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
- return false;
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
+ final void externalPush(ForkJoinTask<?> task) {
+ WorkQueue[] ws; WorkQueue q; Submitter z; int m; ForkJoinTask<?>[] a;
+ if ((z = submitters.get()) != null && plock > 0 &&
+ (ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
+ int b = q.base, s = q.top, n, an;
+ if ((a = q.array) != null && (an = a.length) > (n = s + 1 - b)) {
+ int j = (((an - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ q.top = s + 1; // push on to deque
+ q.qlock = 0;
+ if (n <= 2)
+ signalWork(q);
+ return;
+ }
+ q.qlock = 0;
}
- return true;
+ fullExternalPush(task);
+ }
+
+ /**
+ * Full version of externalPush. This method is called, among
+ * other times, upon the first submission of the first task to the
+ * pool, so must perform secondary initialization (via
+ * initWorkers). It also detects first submission by an external
+ * thread by looking up its ThreadLocal, and creates a new shared
+ * queue if the one at index if empty or contended. The plock lock
+ * body must be exception-free (so no try/finally) so we
+ * optimistically allocate new queues outside the lock and throw
+ * them away if (very rarely) not needed.
+ */
+ private void fullExternalPush(ForkJoinTask<?> task) {
+ int r = 0; // random index seed
+ for (Submitter z = submitters.get();;) {
+ WorkQueue[] ws; WorkQueue q; int ps, m, k;
+ if (z == null) {
+ if (U.compareAndSwapInt(this, INDEXSEED, r = indexSeed,
+ r += SEED_INCREMENT) && r != 0)
+ submitters.set(z = new Submitter(r));
+ }
+ else if (r == 0) { // move to a different index
+ r = z.seed;
+ r ^= r << 13; // same xorshift as WorkQueues
+ r ^= r >>> 17;
+ z.seed = r ^ (r << 5);
+ }
+ else if ((ps = plock) < 0)
+ throw new RejectedExecutionException();
+ else if (ps == 0 || (ws = workQueues) == null ||
+ (m = ws.length - 1) < 0)
+ initWorkers();
+ else if ((q = ws[k = r & m & SQMASK]) != null) {
+ if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ ForkJoinTask<?>[] a = q.array;
+ int s = q.top;
+ boolean submitted = false;
+ try { // locked version of push
+ if ((a != null && a.length > s + 1 - q.base) ||
+ (a = q.growArray()) != null) { // must presize
+ int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
+ U.putOrderedObject(a, j, task);
+ q.top = s + 1;
+ submitted = true;
+ }
+ } finally {
+ q.qlock = 0; // unlock
+ }
+ if (submitted) {
+ signalWork(q);
+ return;
+ }
+ }
+ r = 0; // move on failure
+ }
+ else if (((ps = plock) & PL_LOCK) == 0) { // create new queue
+ q = new WorkQueue(this, null, SHARED_QUEUE, r);
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if ((ws = workQueues) != null && k < ws.length && ws[k] == null)
+ ws[k] = q;
+ int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
+ if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
+ releasePlock(nps);
+ }
+ else
+ r = 0; // try elsewhere while lock held
+ }
+ }
+
+ // Maintaining ctl counts
+
+ /**
+ * Increments active count; mainly called upon return from blocking.
+ */
+ final void incrementActiveCount() {
+ long c;
+ do {} while (!U.compareAndSwapLong(this, CTL, c = ctl, c + AC_UNIT));
+ }
+
+ /**
+ * Tries to create or activate a worker if too few are active.
+ *
+ * @param q the (non-null) queue holding tasks to be signalled
+ */
+ final void signalWork(WorkQueue q) {
+ int hint = q.poolIndex;
+ long c; int e, u, i, n; WorkQueue[] ws; WorkQueue w; Thread p;
+ while ((u = (int)((c = ctl) >>> 32)) < 0) {
+ if ((e = (int)c) > 0) {
+ if ((ws = workQueues) != null && ws.length > (i = e & SMASK) &&
+ (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.hint = hint;
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ break;
+ }
+ if (q.top - q.base <= 0)
+ break;
+ }
+ else
+ break;
+ }
+ else {
+ if ((short)u < 0)
+ tryAddWorker();
+ break;
+ }
+ }
}
// Scanning for tasks
/**
- * Scans for and, if found, executes one task. Scans start at a
- * random index of workers array, and randomly select the first
- * (2*#workers)-1 probes, and then, if all empty, resort to 2
- * circular sweeps, which is necessary to check quiescence. and
- * taking a submission only if no stealable tasks were found. The
- * steal code inside the loop is a specialized form of
- * ForkJoinWorkerThread.deqTask, followed bookkeeping to support
- * helpJoinTask and signal propagation. The code for submission
- * queues is almost identical. On each steal, the worker completes
- * not only the task, but also all local tasks that this task may
- * have generated. On detecting staleness or contention when
- * trying to take a task, this method returns without finishing
- * sweep, which allows global state rechecks before retry.
- *
- * @param w the worker
- * @param a the number of active workers
- * @return true if swept all queues without finding a task
+ * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
*/
- private boolean scan(ForkJoinWorkerThread w, int a) {
- int g = scanGuard; // mask 0 avoids useless scans if only one active
- int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK;
- ForkJoinWorkerThread[] ws = workers;
- if (ws == null || ws.length <= m) // staleness check
- return false;
- for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- ForkJoinWorkerThread v = ws[k & m];
- if (v != null && (b = v.queueBase) != v.queueTop &&
- (q = v.queue) != null && (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null && v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- int d = (v.queueBase = b + 1) - v.queueTop;
- v.stealHint = w.poolIndex;
- if (d != 0)
- signalWork(); // propagate if nonempty
- w.execTask(t);
- }
- r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5);
- return false; // store next seed
- }
- else if (j < 0) { // xorshift
- r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5;
- }
- else
- ++k;
- }
- if (scanGuard != g) // staleness check
- return false;
- else { // try to take submission
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- if ((b = queueBase) != queueTop &&
- (q = submissionQueue) != null &&
- (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null && queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueBase = b + 1;
- w.execTask(t);
- }
- return false;
- }
- return true; // all queues empty
- }
+ final void runWorker(WorkQueue w) {
+ w.growArray(); // allocate queue
+ do { w.runTask(scan(w)); } while (w.qlock >= 0);
}
/**
- * Tries to enqueue worker w in wait queue and await change in
- * worker's eventCount. If the pool is quiescent and there is
- * more than one worker, possibly terminates worker upon exit.
- * Otherwise, before blocking, rescans queues to avoid missed
- * signals. Upon finding work, releases at least one worker
- * (which may be the current worker). Rescans restart upon
- * detected staleness or failure to release due to
- * contention. Note the unusual conventions about Thread.interrupt
- * here and elsewhere: Because interrupts are used solely to alert
- * threads to check termination, which is checked here anyway, we
- * clear status (using Thread.interrupted) before any call to
- * park, so that park does not immediately return due to status
- * being set via some other unrelated call to interrupt in user
- * code.
+ * Scans for and, if found, returns one task, else possibly
+ * inactivates the worker. This method operates on single reads of
+ * volatile state and is designed to be re-invoked continuously,
+ * in part because it returns upon detecting inconsistencies,
+ * contention, or state changes that indicate possible success on
+ * re-invocation.
*
- * @param w the calling worker
- * @param c the ctl value on entry
- * @return true if waited or another thread was released upon enq
+ * The scan searches for tasks across queues (starting at a random
+ * index, and relying on registerWorker to irregularly scatter
+ * them within array to avoid bias), checking each at least twice.
+ * The scan terminates upon either finding a non-empty queue, or
+ * completing the sweep. If the worker is not inactivated, it
+ * takes and returns a task from this queue. Otherwise, if not
+ * activated, it signals workers (that may include itself) and
+ * returns so caller can retry. Also returns for true if the
+ * worker array may have changed during an empty scan. On failure
+ * to find a task, we take one of the following actions, after
+ * which the caller will retry calling this method unless
+ * terminated.
+ *
+ * * If pool is terminating, terminate the worker.
+ *
+ * * If not already enqueued, try to inactivate and enqueue the
+ * worker on wait queue. Or, if inactivating has caused the pool
+ * to be quiescent, relay to idleAwaitWork to possibly shrink
+ * pool.
+ *
+ * * If already enqueued and none of the above apply, possibly
+ * park awaiting signal, else lingering to help scan and signal.
+ *
+ * * If a non-empty queue discovered or left as a hint,
+ * help wake up other workers before return
+ *
+ * @param w the worker (via its WorkQueue)
+ * @return a task or null if none found
*/
- private boolean tryAwaitWork(ForkJoinWorkerThread w, long c) {
- int v = w.eventCount;
- w.nextWait = (int)c; // w's successor record
- long nc = (long)(v & E_MASK) | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
- if (ctl != c || !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- long d = ctl; // return true if lost to a deq, to force scan
- return (int)d != (int)c && ((d - c) & AC_MASK) >= 0L;
- }
- for (int sc = w.stealCount; sc != 0;) { // accumulate stealCount
- long s = stealCount;
- if (UNSAFE.compareAndSwapLong(this, stealCountOffset, s, s + sc))
- sc = w.stealCount = 0;
- else if (w.eventCount != v)
- return true; // update next time
- }
- if ((!shutdown || !tryTerminate(false)) &&
- (int)c != 0 && parallelism + (int)(nc >> AC_SHIFT) == 0 &&
- blockedCount == 0 && quiescerCount == 0)
- idleAwaitWork(w, nc, c, v); // quiescent
- for (boolean rescanned = false;;) {
- if (w.eventCount != v)
- return true;
- if (!rescanned) {
- int g = scanGuard, m = g & SMASK;
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null && m < ws.length) {
- rescanned = true;
- for (int i = 0; i <= m; ++i) {
- ForkJoinWorkerThread u = ws[i];
- if (u != null) {
- if (u.queueBase != u.queueTop &&
- !tryReleaseWaiter())
- rescanned = false; // contended
- if (w.eventCount != v)
- return true;
- }
+ private final ForkJoinTask<?> scan(WorkQueue w) {
+ WorkQueue[] ws; int m;
+ int ps = plock; // read plock before ws
+ if (w != null && (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+ int ec = w.eventCount; // ec is negative if inactive
+ int r = w.seed; r ^= r << 13; r ^= r >>> 17; w.seed = r ^= r << 5;
+ w.hint = -1; // update seed and clear hint
+ int j = ((m + m + 1) | MIN_SCAN) & MAX_SCAN;
+ do {
+ WorkQueue q; ForkJoinTask<?>[] a; int b;
+ if ((q = ws[(r + j) & m]) != null && (b = q.base) - q.top < 0 &&
+ (a = q.array) != null) { // probably nonempty
+ int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, i);
+ if (q.base == b && ec >= 0 && t != null &&
+ U.compareAndSwapObject(a, i, t, null)) {
+ if ((q.base = b + 1) - q.top < 0)
+ signalWork(q);
+ return t; // taken
+ }
+ else if ((ec < 0 || j < m) && (int)(ctl >> AC_SHIFT) <= 0) {
+ w.hint = (r + j) & m; // help signal below
+ break; // cannot take
}
}
- if (scanGuard != g || // stale
- (queueBase != queueTop && !tryReleaseWaiter()))
- rescanned = false;
- if (!rescanned)
- Thread.yield(); // reduce contention
- else
- Thread.interrupted(); // clear before park
+ } while (--j >= 0);
+
+ int h, e, ns; long c, sc; WorkQueue q;
+ if ((ns = w.nsteals) != 0) {
+ if (U.compareAndSwapLong(this, STEALCOUNT,
+ sc = stealCount, sc + ns))
+ w.nsteals = 0; // collect steals and rescan
}
+ else if (plock != ps) // consistency check
+ ; // skip
+ else if ((e = (int)(c = ctl)) < 0)
+ w.qlock = -1; // pool is terminating
else {
- w.parked = true; // must recheck
- if (w.eventCount != v) {
- w.parked = false;
- return true;
+ if ((h = w.hint) < 0) {
+ if (ec >= 0) { // try to enqueue/inactivate
+ long nc = (((long)ec |
+ ((c - AC_UNIT) & (AC_MASK|TC_MASK))));
+ w.nextWait = e; // link and mark inactive
+ w.eventCount = ec | INT_SIGN;
+ if (ctl != c || !U.compareAndSwapLong(this, CTL, c, nc))
+ w.eventCount = ec; // unmark on CAS failure
+ else if ((int)(c >> AC_SHIFT) == 1 - (config & SMASK))
+ idleAwaitWork(w, nc, c);
+ }
+ else if (w.eventCount < 0 && !tryTerminate(false, false) &&
+ ctl == c) { // block
+ Thread wt = Thread.currentThread();
+ Thread.interrupted(); // clear status
+ U.putObject(wt, PARKBLOCKER, this);
+ w.parker = wt; // emulate LockSupport.park
+ if (w.eventCount < 0) // recheck
+ U.park(false, 0L);
+ w.parker = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ }
}
- LockSupport.park(this);
- rescanned = w.parked = false;
+ if ((h >= 0 || (h = w.hint) >= 0) &&
+ (ws = workQueues) != null && h < ws.length &&
+ (q = ws[h]) != null) { // signal others before retry
+ WorkQueue v; Thread p; int u, i, s;
+ for (int n = (config & SMASK) >>> 1;;) {
+ int idleCount = (w.eventCount < 0) ? 0 : -1;
+ if (((s = idleCount - q.base + q.top) <= n &&
+ (n = s) <= 0) ||
+ (u = (int)((c = ctl) >>> 32)) >= 0 ||
+ (e = (int)c) <= 0 || m < (i = e & SMASK) ||
+ (v = ws[i]) == null)
+ break;
+ long nc = (((long)(v.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (v.eventCount != (e | INT_SIGN) ||
+ !U.compareAndSwapLong(this, CTL, c, nc))
+ break;
+ v.hint = h;
+ v.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = v.parker) != null)
+ U.unpark(p);
+ if (--n <= 0)
+ break;
+ }
+ }
}
}
+ return null;
}
/**
- * If inactivating worker w has caused pool to become
- * quiescent, check for pool termination, and wait for event
- * for up to SHRINK_RATE nanosecs (rescans are unnecessary in
- * this case because quiescence reflects consensus about lack
- * of work). On timeout, if ctl has not changed, terminate the
- * worker. Upon its termination (see deregisterWorker), it may
- * wake up another worker to possibly repeat this process.
+ * If inactivating worker w has caused the pool to become
+ * quiescent, checks for pool termination, and, so long as this is
+ * not the only worker, waits for event for up to a given
+ * duration. On timeout, if ctl has not changed, terminates the
+ * worker, which will in turn wake up another worker to possibly
+ * repeat this process.
*
* @param w the calling worker
- * @param currentCtl the ctl value after enqueuing w
- * @param prevCtl the ctl value if w terminated
- * @param v the eventCount w awaits change
+ * @param currentCtl the ctl value triggering possible quiescence
+ * @param prevCtl the ctl value to restore if thread is terminated
*/
- private void idleAwaitWork(ForkJoinWorkerThread w, long currentCtl,
- long prevCtl, int v) {
- if (w.eventCount == v) {
- if (shutdown)
- tryTerminate(false);
- ForkJoinTask.helpExpungeStaleExceptions(); // help clean weak refs
+ private void idleAwaitWork(WorkQueue w, long currentCtl, long prevCtl) {
+ if (w != null && w.eventCount < 0 &&
+ !tryTerminate(false, false) && (int)prevCtl != 0) {
+ int dc = -(short)(currentCtl >>> TC_SHIFT);
+ long parkTime = dc < 0 ? FAST_IDLE_TIMEOUT: (dc + 1) * IDLE_TIMEOUT;
+ long deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
+ Thread wt = Thread.currentThread();
while (ctl == currentCtl) {
- long startTime = System.nanoTime();
- w.parked = true;
- if (w.eventCount == v) // must recheck
- LockSupport.parkNanos(this, SHRINK_RATE);
- w.parked = false;
- if (w.eventCount != v)
+ Thread.interrupted(); // timed variant of version in scan()
+ U.putObject(wt, PARKBLOCKER, this);
+ w.parker = wt;
+ if (ctl == currentCtl)
+ U.park(false, parkTime);
+ w.parker = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ if (ctl != currentCtl)
break;
- else if (System.nanoTime() - startTime <
- SHRINK_RATE - (SHRINK_RATE / 10)) // timing slop
- Thread.interrupted(); // spurious wakeup
- else if (UNSAFE.compareAndSwapLong(this, ctlOffset,
- currentCtl, prevCtl)) {
- w.terminate = true; // restore previous
- w.eventCount = ((int)currentCtl + EC_UNIT) & E_MASK;
+ if (deadline - System.nanoTime() <= 0L &&
+ U.compareAndSwapLong(this, CTL, currentCtl, prevCtl)) {
+ w.eventCount = (w.eventCount + E_SEQ) | E_MASK;
+ w.qlock = -1; // shrink
break;
}
}
}
}
- // Submissions
-
/**
- * Enqueues the given task in the submissionQueue. Same idea as
- * ForkJoinWorkerThread.pushTask except for use of submissionLock.
- *
- * @param t the task
- */
- private void addSubmission(ForkJoinTask<?> t) {
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- ForkJoinTask<?>[] q; int s, m;
- if ((q = submissionQueue) != null) { // ignore if queue removed
- long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE;
- UNSAFE.putOrderedObject(q, u, t);
- queueTop = s + 1;
- if (s - queueBase == m)
- growSubmissionQueue();
- }
- } finally {
- lock.unlock();
- }
- signalWork();
- }
-
- // (pollSubmission is defined below with exported methods)
-
- /**
- * Creates or doubles submissionQueue array.
- * Basically identical to ForkJoinWorkerThread version.
- */
- private void growSubmissionQueue() {
- ForkJoinTask<?>[] oldQ = submissionQueue;
- int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
- if (size > MAXIMUM_QUEUE_CAPACITY)
- throw new RejectedExecutionException("Queue capacity exceeded");
- if (size < INITIAL_QUEUE_CAPACITY)
- size = INITIAL_QUEUE_CAPACITY;
- ForkJoinTask<?>[] q = submissionQueue = new ForkJoinTask<?>[size];
- int mask = size - 1;
- int top = queueTop;
- int oldMask;
- if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
- for (int b = queueBase; b != top; ++b) {
- long u = ((b & oldMask) << ASHIFT) + ABASE;
- Object x = UNSAFE.getObjectVolatile(oldQ, u);
- if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
- UNSAFE.putObjectVolatile
- (q, ((b & mask) << ASHIFT) + ABASE, x);
- }
- }
- }
-
- // Blocking support
-
- /**
- * Tries to increment blockedCount, decrement active count
- * (sometimes implicitly) and possibly release or create a
- * compensating worker in preparation for blocking. Fails
- * on contention or termination.
+ * Scans through queues looking for work while joining a task; if
+ * any present, signals. May return early if more signalling is
+ * detectably unneeded.
*
- * @return true if the caller can block, else should recheck and retry
- */
- private boolean tryPreBlock() {
- int b = blockedCount;
- if (UNSAFE.compareAndSwapInt(this, blockedCountOffset, b, b + 1)) {
- int pc = parallelism;
- do {
- ForkJoinWorkerThread[] ws; ForkJoinWorkerThread w;
- int e, ac, tc, rc, i;
- long c = ctl;
- int u = (int)(c >>> 32);
- if ((e = (int)c) < 0) {
- // skip -- terminating
- }
- else if ((ac = (u >> UAC_SHIFT)) <= 0 && e != 0 &&
- (ws = workers) != null &&
- (i = ~e & SMASK) < ws.length &&
- (w = ws[i]) != null) {
- long nc = ((long)(w.nextWait & E_MASK) |
- (c & (AC_MASK|TC_MASK)));
- if (w.eventCount == e &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- w.eventCount = (e + EC_UNIT) & E_MASK;
- if (w.parked)
- UNSAFE.unpark(w);
- return true; // release an idle worker
- }
- }
- else if ((tc = (short)(u >>> UTC_SHIFT)) >= 0 && ac + pc > 1) {
- long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc))
- return true; // no compensation needed
- }
- else if (tc + pc < MAX_ID) {
- long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) {
- addWorker();
- return true; // create a replacement
- }
- }
- // try to back out on any failure and let caller retry
- } while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
- b = blockedCount, b - 1));
- }
- return false;
- }
-
- /**
- * Decrements blockedCount and increments active count
- */
- private void postBlock() {
- long c;
- do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, // no mask
- c = ctl, c + AC_UNIT));
- int b;
- do {} while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset,
- b = blockedCount, b - 1));
- }
-
- /**
- * Possibly blocks waiting for the given task to complete, or
- * cancels the task if terminating. Fails to wait if contended.
- *
- * @param joinMe the task
+ * @param task return early if done
+ * @param origin an index to start scan
*/
- final void tryAwaitJoin(ForkJoinTask<?> joinMe) {
- int s;
- Thread.interrupted(); // clear interrupts before checking termination
- if (joinMe.status >= 0) {
- if (tryPreBlock()) {
- joinMe.tryAwaitDone(0L);
- postBlock();
- }
- else if ((ctl & STOP_BIT) != 0L)
- joinMe.cancelIgnoringExceptions();
- }
- }
-
- /**
- * Possibly blocks the given worker waiting for joinMe to
- * complete or timeout
- *
- * @param joinMe the task
- * @param millis the wait time for underlying Object.wait
- */
- final void timedAwaitJoin(ForkJoinTask<?> joinMe, long nanos) {
- while (joinMe.status >= 0) {
- Thread.interrupted();
- if ((ctl & STOP_BIT) != 0L) {
- joinMe.cancelIgnoringExceptions();
- break;
- }
- if (tryPreBlock()) {
- long last = System.nanoTime();
- while (joinMe.status >= 0) {
- long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
- if (millis <= 0)
- break;
- joinMe.tryAwaitDone(millis);
- if (joinMe.status < 0)
- break;
- if ((ctl & STOP_BIT) != 0L) {
- joinMe.cancelIgnoringExceptions();
+ private void helpSignal(ForkJoinTask<?> task, int origin) {
+ WorkQueue[] ws; WorkQueue w; Thread p; long c; int m, u, e, i, s;
+ if (task != null && task.status >= 0 &&
+ (u = (int)(ctl >>> 32)) < 0 && (u >> UAC_SHIFT) < 0 &&
+ (ws = workQueues) != null && (m = ws.length - 1) >= 0) {
+ outer: for (int k = origin, j = m; j >= 0; --j) {
+ WorkQueue q = ws[k++ & m];
+ for (int n = m;;) { // limit to at most m signals
+ if (task.status < 0)
+ break outer;
+ if (q == null ||
+ ((s = -q.base + q.top) <= n && (n = s) <= 0))
break;
- }
- long now = System.nanoTime();
- nanos -= now - last;
- last = now;
- }
- postBlock();
- break;
- }
- }
- }
-
- /**
- * If necessary, compensates for blocker, and blocks
- */
- private void awaitBlocker(ManagedBlocker blocker)
- throws InterruptedException {
- while (!blocker.isReleasable()) {
- if (tryPreBlock()) {
- try {
- do {} while (!blocker.isReleasable() && !blocker.block());
- } finally {
- postBlock();
- }
- break;
- }
- }
- }
-
- // Creating, registering and deregistring workers
-
- /**
- * Tries to create and start a worker; minimally rolls back counts
- * on failure.
- */
- private void addWorker() {
- Throwable ex = null;
- ForkJoinWorkerThread t = null;
- try {
- t = factory.newThread(this);
- } catch (Throwable e) {
- ex = e;
- }
- if (t == null) { // null or exceptional factory return
- long c; // adjust counts
- do {} while (!UNSAFE.compareAndSwapLong
- (this, ctlOffset, c = ctl,
- (((c - AC_UNIT) & AC_MASK) |
- ((c - TC_UNIT) & TC_MASK) |
- (c & ~(AC_MASK|TC_MASK)))));
- // Propagate exception if originating from an external caller
- if (!tryTerminate(false) && ex != null &&
- !(Thread.currentThread() instanceof ForkJoinWorkerThread))
- UNSAFE.throwException(ex);
- }
- else
- t.start();
- }
-
- /**
- * Callback from ForkJoinWorkerThread constructor to assign a
- * public name
- */
- final String nextWorkerName() {
- for (int n;;) {
- if (UNSAFE.compareAndSwapInt(this, nextWorkerNumberOffset,
- n = nextWorkerNumber, ++n))
- return workerNamePrefix + n;
- }
- }
-
- /**
- * Callback from ForkJoinWorkerThread constructor to
- * determine its poolIndex and record in workers array.
- *
- * @param w the worker
- * @return the worker's pool index
- */
- final int registerWorker(ForkJoinWorkerThread w) {
- /*
- * In the typical case, a new worker acquires the lock, uses
- * next available index and returns quickly. Since we should
- * not block callers (ultimately from signalWork or
- * tryPreBlock) waiting for the lock needed to do this, we
- * instead help release other workers while waiting for the
- * lock.
- */
- for (int g;;) {
- ForkJoinWorkerThread[] ws;
- if (((g = scanGuard) & SG_UNIT) == 0 &&
- UNSAFE.compareAndSwapInt(this, scanGuardOffset,
- g, g | SG_UNIT)) {
- int k = nextWorkerIndex;
- try {
- if ((ws = workers) != null) { // ignore on shutdown
- int n = ws.length;
- if (k < 0 || k >= n || ws[k] != null) {
- for (k = 0; k < n && ws[k] != null; ++k)
- ;
- if (k == n)
- ws = workers = Arrays.copyOf(ws, n << 1);
- }
- ws[k] = w;
- nextWorkerIndex = k + 1;
- int m = g & SMASK;
- g = (k > m) ? ((m << 1) + 1) & SMASK : g + (SG_UNIT<<1);
- }
- } finally {
- scanGuard = g;
- }
- return k;
- }
- else if ((ws = workers) != null) { // help release others
- for (ForkJoinWorkerThread u : ws) {
- if (u != null && u.queueBase != u.queueTop) {
- if (tryReleaseWaiter())
+ if ((u = (int)((c = ctl) >>> 32)) >= 0 ||
+ (e = (int)c) <= 0 || m < (i = e & SMASK) ||
+ (w = ws[i]) == null)
+ break outer;
+ long nc = (((long)(w.nextWait & E_MASK)) |
+ ((long)(u + UAC_UNIT) << 32));
+ if (w.eventCount != (e | INT_SIGN))
+ break outer;
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ if (--n <= 0)
break;
}
}
@@ -1197,202 +1881,631 @@
}
/**
- * Final callback from terminating worker. Removes record of
- * worker from array, and adjusts counts. If pool is shutting
- * down, tries to complete termination.
- *
- * @param w the worker
- */
- final void deregisterWorker(ForkJoinWorkerThread w, Throwable ex) {
- int idx = w.poolIndex;
- int sc = w.stealCount;
- int steps = 0;
- // Remove from array, adjust worker counts and collect steal count.
- // We can intermix failed removes or adjusts with steal updates
- do {
- long s, c;
- int g;
- if (steps == 0 && ((g = scanGuard) & SG_UNIT) == 0 &&
- UNSAFE.compareAndSwapInt(this, scanGuardOffset,
- g, g |= SG_UNIT)) {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null && idx >= 0 &&
- idx < ws.length && ws[idx] == w)
- ws[idx] = null; // verify
- nextWorkerIndex = idx;
- scanGuard = g + SG_UNIT;
- steps = 1;
- }
- if (steps == 1 &&
- UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
- (((c - AC_UNIT) & AC_MASK) |
- ((c - TC_UNIT) & TC_MASK) |
- (c & ~(AC_MASK|TC_MASK)))))
- steps = 2;
- if (sc != 0 &&
- UNSAFE.compareAndSwapLong(this, stealCountOffset,
- s = stealCount, s + sc))
- sc = 0;
- } while (steps != 2 || sc != 0);
- if (!tryTerminate(false)) {
- if (ex != null) // possibly replace if died abnormally
- signalWork();
- else
- tryReleaseWaiter();
- }
- }
-
- // Shutdown and termination
-
- /**
- * Possibly initiates and/or completes termination.
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers, Traces currentSteal ->
+ * currentJoin links looking for a thread working on a descendant
+ * of the given task and with a non-empty queue to steal back and
+ * execute tasks from. The first call to this method upon a
+ * waiting join will often entail scanning/search, (which is OK
+ * because the joiner has nothing better to do), but this method
+ * leaves hints in workers to speed up subsequent calls. The
+ * implementation is very branchy to cope with potential
+ * inconsistencies or loops encountering chains that are stale,
+ * unknown, or so long that they are likely cyclic.
*
- * @param now if true, unconditionally terminate, else only
- * if shutdown and empty queue and no active workers
- * @return true if now terminating or terminated
+ * @param joiner the joining worker
+ * @param task the task to join
+ * @return 0 if no progress can be made, negative if task
+ * known complete, else positive
*/
- private boolean tryTerminate(boolean now) {
- long c;
- while (((c = ctl) & STOP_BIT) == 0) {
- if (!now) {
- if ((int)(c >> AC_SHIFT) != -parallelism)
- return false;
- if (!shutdown || blockedCount != 0 || quiescerCount != 0 ||
- queueBase != queueTop) {
- if (ctl == c) // staleness check
- return false;
- continue;
- }
- }
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, c | STOP_BIT))
- startTerminating();
- }
- if ((short)(c >>> TC_SHIFT) == -parallelism) { // signal when 0 workers
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- termination.signalAll();
- } finally {
- lock.unlock();
- }
- }
- return true;
- }
-
- /**
- * Runs up to three passes through workers: (0) Setting
- * termination status for each worker, followed by wakeups up to
- * queued workers; (1) helping cancel tasks; (2) interrupting
- * lagging threads (likely in external tasks, but possibly also
- * blocked in joins). Each pass repeats previous steps because of
- * potential lagging thread creation.
- */
- private void startTerminating() {
- cancelSubmissions();
- for (int pass = 0; pass < 3; ++pass) {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null) {
- for (ForkJoinWorkerThread w : ws) {
- if (w != null) {
- w.terminate = true;
- if (pass > 0) {
- w.cancelTasks();
- if (pass > 1 && !w.isInterrupted()) {
- try {
- w.interrupt();
- } catch (SecurityException ignore) {
- }
+ private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) {
+ int stat = 0, steps = 0; // bound to avoid cycles
+ if (joiner != null && task != null) { // hoist null checks
+ restart: for (;;) {
+ ForkJoinTask<?> subtask = task; // current target
+ for (WorkQueue j = joiner, v;;) { // v is stealer of subtask
+ WorkQueue[] ws; int m, s, h;
+ if ((s = task.status) < 0) {
+ stat = s;
+ break restart;
+ }
+ if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
+ break restart; // shutting down
+ if ((v = ws[h = (j.hint | 1) & m]) == null ||
+ v.currentSteal != subtask) {
+ for (int origin = h;;) { // find stealer
+ if (((h = (h + 2) & m) & 15) == 1 &&
+ (subtask.status < 0 || j.currentJoin != subtask))
+ continue restart; // occasional staleness check
+ if ((v = ws[h]) != null &&
+ v.currentSteal == subtask) {
+ j.hint = h; // save hint
+ break;
+ }
+ if (h == origin)
+ break restart; // cannot find stealer
+ }
+ }
+ for (;;) { // help stealer or descend to its stealer
+ ForkJoinTask[] a; int b;
+ if (subtask.status < 0) // surround probes with
+ continue restart; // consistency checks
+ if ((b = v.base) - v.top < 0 && (a = v.array) != null) {
+ int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
+ ForkJoinTask<?> t =
+ (ForkJoinTask<?>)U.getObjectVolatile(a, i);
+ if (subtask.status < 0 || j.currentJoin != subtask ||
+ v.currentSteal != subtask)
+ continue restart; // stale
+ stat = 1; // apparent progress
+ if (t != null && v.base == b &&
+ U.compareAndSwapObject(a, i, t, null)) {
+ v.base = b + 1; // help stealer
+ joiner.runSubtask(t);
+ }
+ else if (v.base == b && ++steps == MAX_HELP)
+ break restart; // v apparently stalled
+ }
+ else { // empty -- try to descend
+ ForkJoinTask<?> next = v.currentJoin;
+ if (subtask.status < 0 || j.currentJoin != subtask ||
+ v.currentSteal != subtask)
+ continue restart; // stale
+ else if (next == null || ++steps == MAX_HELP)
+ break restart; // dead-end or maybe cyclic
+ else {
+ subtask = next;
+ j = v;
+ break;
}
}
}
}
- terminateWaiters();
+ }
+ }
+ return stat;
+ }
+
+ /**
+ * Analog of tryHelpStealer for CountedCompleters. Tries to steal
+ * and run tasks within the target's computation.
+ *
+ * @param task the task to join
+ * @param mode if shared, exit upon completing any task
+ * if all workers are active
+ *
+ */
+ private int helpComplete(ForkJoinTask<?> task, int mode) {
+ WorkQueue[] ws; WorkQueue q; int m, n, s, u;
+ if (task != null && (ws = workQueues) != null &&
+ (m = ws.length - 1) >= 0) {
+ for (int j = 1, origin = j;;) {
+ if ((s = task.status) < 0)
+ return s;
+ if ((q = ws[j & m]) != null && q.pollAndExecCC(task)) {
+ origin = j;
+ if (mode == SHARED_QUEUE &&
+ ((u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0))
+ break;
+ }
+ else if ((j = (j + 2) & m) == origin)
+ break;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Tries to decrement active count (sometimes implicitly) and
+ * possibly release or create a compensating worker in preparation
+ * for blocking. Fails on contention or termination. Otherwise,
+ * adds a new thread if no idle workers are available and pool
+ * may become starved.
+ */
+ final boolean tryCompensate() {
+ int pc = config & SMASK, e, i, tc; long c;
+ WorkQueue[] ws; WorkQueue w; Thread p;
+ if ((ws = workQueues) != null && (e = (int)(c = ctl)) >= 0) {
+ if (e != 0 && (i = e & SMASK) < ws.length &&
+ (w = ws[i]) != null && w.eventCount == (e | INT_SIGN)) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ (c & (AC_MASK|TC_MASK)));
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ return true; // replace with idle worker
+ }
+ }
+ else if ((tc = (short)(c >>> TC_SHIFT)) >= 0 &&
+ (int)(c >> AC_SHIFT) + pc > 1) {
+ long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
+ if (U.compareAndSwapLong(this, CTL, c, nc))
+ return true; // no compensation
+ }
+ else if (tc + pc < MAX_CAP) {
+ long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ ForkJoinWorkerThreadFactory fac;
+ Throwable ex = null;
+ ForkJoinWorkerThread wt = null;
+ try {
+ if ((fac = factory) != null &&
+ (wt = fac.newThread(this)) != null) {
+ wt.start();
+ return true;
+ }
+ } catch (Throwable rex) {
+ ex = rex;
+ }
+ deregisterWorker(wt, ex); // clean up and return false
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helps and/or blocks until the given task is done.
+ *
+ * @param joiner the joining worker
+ * @param task the task
+ * @return task status on exit
+ */
+ final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
+ int s = 0;
+ if (joiner != null && task != null && (s = task.status) >= 0) {
+ ForkJoinTask<?> prevJoin = joiner.currentJoin;
+ joiner.currentJoin = task;
+ do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
+ joiner.tryRemoveAndExec(task)); // process local tasks
+ if (s >= 0 && (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 &&
+ (task instanceof CountedCompleter))
+ s = helpComplete(task, LIFO_QUEUE);
+ }
+ while (s >= 0 && (s = task.status) >= 0) {
+ if ((!joiner.isEmpty() || // try helping
+ (s = tryHelpStealer(joiner, task)) == 0) &&
+ (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 && tryCompensate()) {
+ if (task.trySetSignal() && (s = task.status) >= 0) {
+ synchronized (task) {
+ if (task.status >= 0) {
+ try { // see ForkJoinTask
+ task.wait(); // for explanation
+ } catch (InterruptedException ie) {
+ }
+ }
+ else
+ task.notifyAll();
+ }
+ }
+ long c; // re-activate
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ }
+ }
+ }
+ joiner.currentJoin = prevJoin;
+ }
+ return s;
+ }
+
+ /**
+ * Stripped-down variant of awaitJoin used by timed joins. Tries
+ * to help join only while there is continuous progress. (Caller
+ * will then enter a timed wait.)
+ *
+ * @param joiner the joining worker
+ * @param task the task
+ */
+ final void helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
+ int s;
+ if (joiner != null && task != null && (s = task.status) >= 0) {
+ ForkJoinTask<?> prevJoin = joiner.currentJoin;
+ joiner.currentJoin = task;
+ do {} while ((s = task.status) >= 0 && !joiner.isEmpty() &&
+ joiner.tryRemoveAndExec(task));
+ if (s >= 0 && (s = task.status) >= 0) {
+ helpSignal(task, joiner.poolIndex);
+ if ((s = task.status) >= 0 &&
+ (task instanceof CountedCompleter))
+ s = helpComplete(task, LIFO_QUEUE);
+ }
+ if (s >= 0 && joiner.isEmpty()) {
+ do {} while (task.status >= 0 &&
+ tryHelpStealer(joiner, task) > 0);
+ }
+ joiner.currentJoin = prevJoin;
+ }
+ }
+
+ /**
+ * Returns a (probably) non-empty steal queue, if one is found
+ * during a random, then cyclic scan, else null. This method must
+ * be retried by caller if, by the time it tries to use the queue,
+ * it is empty.
+ * @param r a (random) seed for scanning
+ */
+ private WorkQueue findNonEmptyStealQueue(int r) {
+ for (WorkQueue[] ws;;) {
+ int ps = plock, m, n;
+ if ((ws = workQueues) == null || (m = ws.length - 1) < 1)
+ return null;
+ for (int j = (m + 1) << 2; ;) {
+ WorkQueue q = ws[(((r + j) << 1) | 1) & m];
+ if (q != null && (n = q.base - q.top) < 0) {
+ if (n < -1)
+ signalWork(q);
+ return q;
+ }
+ else if (--j < 0) {
+ if (plock == ps)
+ return null;
+ break;
+ }
}
}
}
/**
- * Polls and cancels all submissions. Called only during termination.
+ * Runs tasks until {@code isQuiescent()}. We piggyback on
+ * active count ctl maintenance, but rather than blocking
+ * when tasks cannot be found, we rescan until all others cannot
+ * find tasks either.
*/
- private void cancelSubmissions() {
- while (queueBase != queueTop) {
- ForkJoinTask<?> task = pollSubmission();
- if (task != null) {
- try {
- task.cancel(false);
- } catch (Throwable ignore) {
+ final void helpQuiescePool(WorkQueue w) {
+ for (boolean active = true;;) {
+ ForkJoinTask<?> localTask; // exhaust local queue
+ while ((localTask = w.nextLocalTask()) != null)
+ localTask.doExec();
+ // Similar to loop in scan(), but ignoring submissions
+ WorkQueue q = findNonEmptyStealQueue(w.nextSeed());
+ if (q != null) {
+ ForkJoinTask<?> t; int b;
+ if (!active) { // re-establish active count
+ long c;
+ active = true;
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ }
+ if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+ w.runSubtask(t);
+ }
+ else {
+ long c;
+ if (active) { // decrement active count without queuing
+ active = false;
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c -= AC_UNIT));
+ }
+ else
+ c = ctl; // re-increment on exit
+ if ((int)(c >> AC_SHIFT) + (config & SMASK) == 0) {
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, c + AC_UNIT));
+ break;
}
}
}
}
/**
- * Tries to set the termination status of waiting workers, and
- * then wakes them up (after which they will terminate).
+ * Gets and removes a local or stolen task for the given worker.
+ *
+ * @return a task, if available
+ */
+ final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
+ for (ForkJoinTask<?> t;;) {
+ WorkQueue q; int b;
+ if ((t = w.nextLocalTask()) != null)
+ return t;
+ if ((q = findNonEmptyStealQueue(w.nextSeed())) == null)
+ return null;
+ if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+ return t;
+ }
+ }
+
+ /**
+ * Returns a cheap heuristic guide for task partitioning when
+ * programmers, frameworks, tools, or languages have little or no
+ * idea about task granularity. In essence by offering this
+ * method, we ask users only about tradeoffs in overhead vs
+ * expected throughput and its variance, rather than how finely to
+ * partition tasks.
+ *
+ * In a steady state strict (tree-structured) computation, each
+ * thread makes available for stealing enough tasks for other
+ * threads to remain active. Inductively, if all threads play by
+ * the same rules, each thread should make available only a
+ * constant number of tasks.
+ *
+ * The minimum useful constant is just 1. But using a value of 1
+ * would require immediate replenishment upon each steal to
+ * maintain enough tasks, which is infeasible. Further,
+ * partitionings/granularities of offered tasks should minimize
+ * steal rates, which in general means that threads nearer the top
+ * of computation tree should generate more than those nearer the
+ * bottom. In perfect steady state, each thread is at
+ * approximately the same level of computation tree. However,
+ * producing extra tasks amortizes the uncertainty of progress and
+ * diffusion assumptions.
+ *
+ * So, users will want to use values larger, but not much larger
+ * than 1 to both smooth over transient shortages and hedge
+ * against uneven progress; as traded off against the cost of
+ * extra task overhead. We leave the user to pick a threshold
+ * value to compare with the results of this call to guide
+ * decisions, but recommend values such as 3.
+ *
+ * When all threads are active, it is on average OK to estimate
+ * surplus strictly locally. In steady-state, if one thread is
+ * maintaining say 2 surplus tasks, then so are others. So we can
+ * just use estimated queue length. However, this strategy alone
+ * leads to serious mis-estimates in some non-steady-state
+ * conditions (ramp-up, ramp-down, other stalls). We can detect
+ * many of these by further considering the number of "idle"
+ * threads, that are known to have zero queued tasks, so
+ * compensate by a factor of (#idle/#active) threads.
+ *
+ * Note: The approximation of #busy workers as #active workers is
+ * not very good under current signalling scheme, and should be
+ * improved.
*/
- private void terminateWaiters() {
- ForkJoinWorkerThread[] ws = workers;
- if (ws != null) {
- ForkJoinWorkerThread w; long c; int i, e;
- int n = ws.length;
- while ((i = ~(e = (int)(c = ctl)) & SMASK) < n &&
- (w = ws[i]) != null && w.eventCount == (e & E_MASK)) {
- if (UNSAFE.compareAndSwapLong(this, ctlOffset, c,
- (long)(w.nextWait & E_MASK) |
- ((c + AC_UNIT) & AC_MASK) |
- (c & (TC_MASK|STOP_BIT)))) {
- w.terminate = true;
- w.eventCount = e + EC_UNIT;
- if (w.parked)
- UNSAFE.unpark(w);
+ static int getSurplusQueuedTaskCount() {
+ Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
+ int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
+ int n = (q = wt.workQueue).top - q.base;
+ int a = (int)(pool.ctl >> AC_SHIFT) + p;
+ return n - (a > (p >>>= 1) ? 0 :
+ a > (p >>>= 1) ? 1 :
+ a > (p >>>= 1) ? 2 :
+ a > (p >>>= 1) ? 4 :
+ 8);
+ }
+ return 0;
+ }
+
+ // Termination
+
+ /**
+ * Possibly initiates and/or completes termination. The caller
+ * triggering termination runs three passes through workQueues:
+ * (0) Setting termination status, followed by wakeups of queued
+ * workers; (1) cancelling all tasks; (2) interrupting lagging
+ * threads (likely in external tasks, but possibly also blocked in
+ * joins). Each pass repeats previous steps because of potential
+ * lagging thread creation.
+ *
+ * @param now if true, unconditionally terminate, else only
+ * if no work and no active workers
+ * @param enable if true, enable shutdown when next possible
+ * @return true if now terminating or terminated
+ */
+ private boolean tryTerminate(boolean now, boolean enable) {
+ if (this == commonPool) // cannot shut down
+ return false;
+ for (long c;;) {
+ if (((c = ctl) & STOP_BIT) != 0) { // already terminating
+ if ((short)(c >>> TC_SHIFT) == -(config & SMASK)) {
+ synchronized (this) {
+ notifyAll(); // signal when 0 workers
+ }
+ }
+ return true;
+ }
+ if (plock >= 0) { // not yet enabled
+ int ps;
+ if (!enable)
+ return false;
+ if (((ps = plock) & PL_LOCK) != 0 ||
+ !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
+ ps = acquirePlock();
+ if (!U.compareAndSwapInt(this, PLOCK, ps, SHUTDOWN))
+ releasePlock(SHUTDOWN);
+ }
+ if (!now) { // check if idle & no tasks
+ if ((int)(c >> AC_SHIFT) != -(config & SMASK) ||
+ hasQueuedSubmissions())
+ return false;
+ // Check for unqueued inactive workers. One pass suffices.
+ WorkQueue[] ws = workQueues; WorkQueue w;
+ if (ws != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && w.eventCount >= 0)
+ return false;
+ }
+ }
+ }
+ if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {
+ for (int pass = 0; pass < 3; ++pass) {
+ WorkQueue[] ws = workQueues;
+ if (ws != null) {
+ WorkQueue w; Thread wt;
+ int n = ws.length;
+ for (int i = 0; i < n; ++i) {
+ if ((w = ws[i]) != null) {
+ w.qlock = -1;
+ if (pass > 0) {
+ w.cancelAll();
+ if (pass > 1 && (wt = w.owner) != null) {
+ if (!wt.isInterrupted()) {
+ try {
+ wt.interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ U.unpark(wt);
+ }
+ }
+ }
+ }
+ // Wake up workers parked on event queue
+ int i, e; long cc; Thread p;
+ while ((e = (int)(cc = ctl) & E_MASK) != 0 &&
+ (i = e & SMASK) < n &&
+ (w = ws[i]) != null) {
+ long nc = ((long)(w.nextWait & E_MASK) |
+ ((cc + AC_UNIT) & AC_MASK) |
+ (cc & (TC_MASK|STOP_BIT)));
+ if (w.eventCount == (e | INT_SIGN) &&
+ U.compareAndSwapLong(this, CTL, cc, nc)) {
+ w.eventCount = (e + E_SEQ) & E_MASK;
+ w.qlock = -1;
+ if ((p = w.parker) != null)
+ U.unpark(p);
+ }
+ }
+ }
}
}
}
}
- // misc ForkJoinWorkerThread support
+ // external operations on common pool
+
+ /**
+ * Returns common pool queue for a thread that has submitted at
+ * least one task.
+ */
+ static WorkQueue commonSubmitterQueue() {
+ ForkJoinPool p; WorkQueue[] ws; int m; Submitter z;
+ return ((z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0) ?
+ ws[m & z.seed & SQMASK] : null;
+ }
/**
- * Increment or decrement quiescerCount. Needed only to prevent
- * triggering shutdown if a worker is transiently inactive while
- * checking quiescence.
- *
- * @param delta 1 for increment, -1 for decrement
+ * Tries to pop the given task from submitter's queue in common pool.
*/
- final void addQuiescerCount(int delta) {
- int c;
- do {} while (!UNSAFE.compareAndSwapInt(this, quiescerCountOffset,
- c = quiescerCount, c + delta));
+ static boolean tryExternalUnpush(ForkJoinTask<?> t) {
+ ForkJoinPool p; WorkQueue[] ws; WorkQueue q; Submitter z;
+ ForkJoinTask<?>[] a; int m, s;
+ if (t != null &&
+ (z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ (s = q.top) != q.base &&
+ (a = q.array) != null) {
+ long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
+ if (U.getObject(a, j) == t &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s && // recheck
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ q.qlock = 0;
+ return true;
+ }
+ q.qlock = 0;
+ }
+ }
+ return false;
}
/**
- * Directly increment or decrement active count without
- * queuing. This method is used to transiently assert inactivation
- * while checking quiescence.
- *
- * @param delta 1 for increment, -1 for decrement
+ * Tries to pop and run local tasks within the same computation
+ * as the given root. On failure, tries to help complete from
+ * other queues via helpComplete.
*/
- final void addActiveCount(int delta) {
- long d = delta < 0 ? -AC_UNIT : AC_UNIT;
- long c;
- do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl,
- ((c + d) & AC_MASK) |
- (c & ~AC_MASK)));
+ private void externalHelpComplete(WorkQueue q, ForkJoinTask<?> root) {
+ ForkJoinTask<?>[] a; int m;
+ if (q != null && (a = q.array) != null && (m = (a.length - 1)) >= 0 &&
+ root != null && root.status >= 0) {
+ for (;;) {
+ int s, u; Object o; CountedCompleter<?> task = null;
+ if ((s = q.top) - q.base > 0) {
+ long j = ((m & (s - 1)) << ASHIFT) + ABASE;
+ if ((o = U.getObject(a, j)) != null &&
+ (o instanceof CountedCompleter)) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o, r = t;
+ do {
+ if (r == root) {
+ if (U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ task = t;
+ }
+ q.qlock = 0;
+ }
+ break;
+ }
+ } while ((r = r.completer) != null);
+ }
+ }
+ if (task != null)
+ task.doExec();
+ if (root.status < 0 ||
+ (u = (int)(ctl >>> 32)) >= 0 || (u >> UAC_SHIFT) >= 0)
+ break;
+ if (task == null) {
+ helpSignal(root, q.poolIndex);
+ if (root.status >= 0)
+ helpComplete(root, SHARED_QUEUE);
+ break;
+ }
+ }
+ }
}
/**
- * Returns the approximate (non-atomic) number of idle threads per
- * active thread.
+ * Tries to help execute or signal availability of the given task
+ * from submitter's queue in common pool.
*/
- final int idlePerActive() {
- // Approximate at powers of two for small values, saturate past 4
- int p = parallelism;
- int a = p + (int)(ctl >> AC_SHIFT);
- return (a > (p >>>= 1) ? 0 :
- a > (p >>>= 1) ? 1 :
- a > (p >>>= 1) ? 2 :
- a > (p >>>= 1) ? 4 :
- 8);
+ static void externalHelpJoin(ForkJoinTask<?> t) {
+ // Some hard-to-avoid overlap with tryExternalUnpush
+ ForkJoinPool p; WorkQueue[] ws; WorkQueue q, w; Submitter z;
+ ForkJoinTask<?>[] a; int m, s, n;
+ if (t != null &&
+ (z = submitters.get()) != null &&
+ (p = commonPool) != null &&
+ (ws = p.workQueues) != null &&
+ (m = ws.length - 1) >= 0 &&
+ (q = ws[m & z.seed & SQMASK]) != null &&
+ (a = q.array) != null) {
+ int am = a.length - 1;
+ if ((s = q.top) != q.base) {
+ long j = ((am & (s - 1)) << ASHIFT) + ABASE;
+ if (U.getObject(a, j) == t &&
+ U.compareAndSwapInt(q, QLOCK, 0, 1)) {
+ if (q.array == a && q.top == s &&
+ U.compareAndSwapObject(a, j, t, null)) {
+ q.top = s - 1;
+ q.qlock = 0;
+ t.doExec();
+ }
+ else
+ q.qlock = 0;
+ }
+ }
+ if (t.status >= 0) {
+ if (t instanceof CountedCompleter)
+ p.externalHelpComplete(q, t);
+ else
+ p.helpSignal(t, q.poolIndex);
+ }
+ }
+ }
+
+ /**
+ * Restricted version of helpQuiescePool for external callers
+ */
+ static void externalHelpQuiescePool() {
+ ForkJoinPool p; ForkJoinTask<?> t; WorkQueue q; int b;
+ if ((p = commonPool) != null &&
+ (q = p.findNonEmptyStealQueue(1)) != null &&
+ (b = q.base) - q.top < 0 &&
+ (t = q.pollAt(b)) != null)
+ t.doExec();
}
// Exported methods
@@ -1464,31 +2577,46 @@
checkPermission();
if (factory == null)
throw new NullPointerException();
- if (parallelism <= 0 || parallelism > MAX_ID)
+ if (parallelism <= 0 || parallelism > MAX_CAP)
throw new IllegalArgumentException();
- this.parallelism = parallelism;
this.factory = factory;
this.ueh = handler;
- this.locallyFifo = asyncMode;
+ this.config = parallelism | (asyncMode ? (FIFO_QUEUE << 16) : 0);
long np = (long)(-parallelism); // offset ctl counts
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
- this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
- // initialize workers array with room for 2*parallelism if possible
- int n = parallelism << 1;
- if (n >= MAX_ID)
- n = MAX_ID;
- else { // See Hackers Delight, sec 3.2, where n < (1 << 16)
- n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8;
- }
- workers = new ForkJoinWorkerThread[n + 1];
- this.submissionLock = new ReentrantLock();
- this.termination = submissionLock.newCondition();
+ int pn = nextPoolId();
StringBuilder sb = new StringBuilder("ForkJoinPool-");
- sb.append(poolNumberGenerator.incrementAndGet());
+ sb.append(Integer.toString(pn));
sb.append("-worker-");
this.workerNamePrefix = sb.toString();
}
+ /**
+ * Constructor for common pool, suitable only for static initialization.
+ * Basically the same as above, but uses smallest possible initial footprint.
+ */
+ ForkJoinPool(int parallelism, long ctl,
+ ForkJoinWorkerThreadFactory factory,
+ Thread.UncaughtExceptionHandler handler) {
+ this.config = parallelism;
+ this.ctl = ctl;
+ this.factory = factory;
+ this.ueh = handler;
+ this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
+ }
+
+ /**
+ * Returns the common pool instance. This pool is statically
+ * constructed; its run state is unaffected by attempts to
+ * {@link #shutdown} or {@link #shutdownNow}.
+ *
+ * @return the common pool instance
+ */
+ public static ForkJoinPool commonPool() {
+ // assert commonPool != null : "static init error";
+ return commonPool;
+ }
+
// Execution methods
/**
@@ -1508,34 +2636,10 @@
* scheduled for execution
*/
public <T> T invoke(ForkJoinTask<T> task) {
- Thread t = Thread.currentThread();
if (task == null)
throw new NullPointerException();
- if (shutdown)
- throw new RejectedExecutionException();
- if ((t instanceof ForkJoinWorkerThread) &&
- ((ForkJoinWorkerThread)t).pool == this)
- return task.invoke(); // bypass submit if in same pool
- else {
- addSubmission(task);
- return task.join();
- }
- }
-
- /**
- * Unless terminating, forks task if within an ongoing FJ
- * computation in the current pool, else submits as external task.
- */
- private <T> void forkOrSubmit(ForkJoinTask<T> task) {
- ForkJoinWorkerThread w;
- Thread t = Thread.currentThread();
- if (shutdown)
- throw new RejectedExecutionException();
- if ((t instanceof ForkJoinWorkerThread) &&
- (w = (ForkJoinWorkerThread)t).pool == this)
- w.pushTask(task);
- else
- addSubmission(task);
+ externalPush(task);
+ return task.join();
}
/**
@@ -1549,7 +2653,7 @@
public void execute(ForkJoinTask<?> task) {
if (task == null)
throw new NullPointerException();
- forkOrSubmit(task);
+ externalPush(task);
}
// AbstractExecutorService methods
@@ -1566,8 +2670,8 @@
if (task instanceof ForkJoinTask<?>) // avoid re-wrap
job = (ForkJoinTask<?>) task;
else
- job = ForkJoinTask.adapt(task, null);
- forkOrSubmit(job);
+ job = new ForkJoinTask.AdaptedRunnableAction(task);
+ externalPush(job);
}
/**
@@ -1582,7 +2686,7 @@
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
- forkOrSubmit(task);
+ externalPush(task);
return task;
}
@@ -1592,10 +2696,8 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Callable<T> task) {
- if (task == null)
- throw new NullPointerException();
- ForkJoinTask<T> job = ForkJoinTask.adapt(task);
- forkOrSubmit(job);
+ ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
+ externalPush(job);
return job;
}
@@ -1605,10 +2707,8 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
- if (task == null)
- throw new NullPointerException();
- ForkJoinTask<T> job = ForkJoinTask.adapt(task, result);
- forkOrSubmit(job);
+ ForkJoinTask<T> job = new ForkJoinTask.AdaptedRunnable<T>(task, result);
+ externalPush(job);
return job;
}
@@ -1624,8 +2724,8 @@
if (task instanceof ForkJoinTask<?>) // avoid re-wrap
job = (ForkJoinTask<?>) task;
else
- job = ForkJoinTask.adapt(task, null);
- forkOrSubmit(job);
+ job = new ForkJoinTask.AdaptedRunnableAction(task);
+ externalPush(job);
return job;
}
@@ -1634,25 +2734,31 @@
* @throws RejectedExecutionException {@inheritDoc}
*/
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
- ArrayList<ForkJoinTask<T>> forkJoinTasks =
- new ArrayList<ForkJoinTask<T>>(tasks.size());
- for (Callable<T> task : tasks)
- forkJoinTasks.add(ForkJoinTask.adapt(task));
- invoke(new InvokeAll<T>(forkJoinTasks));
-
+ // In previous versions of this class, this method constructed
+ // a task to run ForkJoinTask.invokeAll, but now external
+ // invocation of multiple tasks is at least as efficient.
+ List<ForkJoinTask<T>> fs = new ArrayList<ForkJoinTask<T>>(tasks.size());
+ // Workaround needed because method wasn't declared with
+ // wildcards in return type but should have been.
@SuppressWarnings({"unchecked", "rawtypes"})
- List<Future<T>> futures = (List<Future<T>>) (List) forkJoinTasks;
- return futures;
- }
+ List<Future<T>> futures = (List<Future<T>>) (List) fs;
- static final class InvokeAll<T> extends RecursiveAction {
- final ArrayList<ForkJoinTask<T>> tasks;
- InvokeAll(ArrayList<ForkJoinTask<T>> tasks) { this.tasks = tasks; }
- public void compute() {
- try { invokeAll(tasks); }
- catch (Exception ignore) {}
+ boolean done = false;
+ try {
+ for (Callable<T> t : tasks) {
+ ForkJoinTask<T> f = new ForkJoinTask.AdaptedCallable<T>(t);
+ externalPush(f);
+ fs.add(f);
+ }
+ for (ForkJoinTask<T> f : fs)
+ f.quietlyJoin();
+ done = true;
+ return futures;
+ } finally {
+ if (!done)
+ for (ForkJoinTask<T> f : fs)
+ f.cancel(false);
}
- private static final long serialVersionUID = -7914297376763021607L;
}
/**
@@ -1680,7 +2786,16 @@
* @return the targeted parallelism level of this pool
*/
public int getParallelism() {
- return parallelism;
+ return config & SMASK;
+ }
+
+ /**
+ * Returns the targeted parallelism level of the common pool.
+ *
+ * @return the targeted parallelism level of the common pool
+ */
+ public static int getCommonPoolParallelism() {
+ return commonPoolParallelism;
}
/**
@@ -1692,7 +2807,7 @@
* @return the number of worker threads
*/
public int getPoolSize() {
- return parallelism + (short)(ctl >>> TC_SHIFT);
+ return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
}
/**
@@ -1702,7 +2817,7 @@
* @return {@code true} if this pool uses async mode
*/
public boolean getAsyncMode() {
- return locallyFifo;
+ return (config >>> 16) == FIFO_QUEUE;
}
/**
@@ -1714,8 +2829,15 @@
* @return the number of worker threads
*/
public int getRunningThreadCount() {
- int r = parallelism + (int)(ctl >> AC_SHIFT);
- return (r <= 0) ? 0 : r; // suppress momentarily negative values
+ int rc = 0;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && w.isApparentlyUnblocked())
+ ++rc;
+ }
+ }
+ return rc;
}
/**
@@ -1726,7 +2848,7 @@
* @return the number of active threads
*/
public int getActiveThreadCount() {
- int r = parallelism + (int)(ctl >> AC_SHIFT) + blockedCount;
+ int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
return (r <= 0) ? 0 : r; // suppress momentarily negative values
}
@@ -1742,7 +2864,7 @@
* @return {@code true} if all threads are currently idle
*/
public boolean isQuiescent() {
- return parallelism + (int)(ctl >> AC_SHIFT) + blockedCount == 0;
+ return (int)(ctl >> AC_SHIFT) + (config & SMASK) == 0;
}
/**
@@ -1757,7 +2879,15 @@
* @return the number of steals
*/
public long getStealCount() {
- return stealCount;
+ long count = stealCount;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.nsteals;
+ }
+ }
+ return count;
}
/**
@@ -1772,12 +2902,12 @@
*/
public long getQueuedTaskCount() {
long count = 0;
- ForkJoinWorkerThread[] ws;
- if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
- (ws = workers) != null) {
- for (ForkJoinWorkerThread w : ws)
- if (w != null)
- count -= w.queueBase - w.queueTop; // must read base first
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.queueSize();
+ }
}
return count;
}
@@ -1790,7 +2920,15 @@
* @return the number of queued submissions
*/
public int getQueuedSubmissionCount() {
- return -queueBase + queueTop;
+ int count = 0;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null)
+ count += w.queueSize();
+ }
+ }
+ return count;
}
/**
@@ -1800,7 +2938,14 @@
* @return {@code true} if there are any queued submissions
*/
public boolean hasQueuedSubmissions() {
- return queueBase != queueTop;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && !w.isEmpty())
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -1811,16 +2956,11 @@
* @return the next submission, or {@code null} if none
*/
protected ForkJoinTask<?> pollSubmission() {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- while ((b = queueBase) != queueTop &&
- (q = submissionQueue) != null &&
- (i = (q.length - 1) & b) >= 0) {
- long u = (i << ASHIFT) + ABASE;
- if ((t = q[i]) != null &&
- queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueBase = b + 1;
- return t;
+ WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; i += 2) {
+ if ((w = ws[i]) != null && (t = w.poll()) != null)
+ return t;
}
}
return null;
@@ -1845,20 +2985,17 @@
*/
protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
int count = 0;
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = pollSubmission();
- if (t != null) {
- c.add(t);
- ++count;
+ WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ while ((t = w.poll()) != null) {
+ c.add(t);
+ ++count;
+ }
+ }
}
}
- ForkJoinWorkerThread[] ws;
- if ((short)(ctl >>> TC_SHIFT) > -parallelism &&
- (ws = workers) != null) {
- for (ForkJoinWorkerThread w : ws)
- if (w != null)
- count += w.drainTasksTo(c);
- }
return count;
}
@@ -1870,21 +3007,36 @@
* @return a string identifying this pool, as well as its state
*/
public String toString() {
- long st = getStealCount();
- long qt = getQueuedTaskCount();
- long qs = getQueuedSubmissionCount();
- int pc = parallelism;
+ // Use a single pass through workQueues to collect counts
+ long qt = 0L, qs = 0L; int rc = 0;
+ long st = stealCount;
long c = ctl;
+ WorkQueue[] ws; WorkQueue w;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ int size = w.queueSize();
+ if ((i & 1) == 0)
+ qs += size;
+ else {
+ qt += size;
+ st += w.nsteals;
+ if (w.isApparentlyUnblocked())
+ ++rc;
+ }
+ }
+ }
+ }
+ int pc = (config & SMASK);
int tc = pc + (short)(c >>> TC_SHIFT);
- int rc = pc + (int)(c >> AC_SHIFT);
- if (rc < 0) // ignore transient negative
- rc = 0;
- int ac = rc + blockedCount;
+ int ac = pc + (int)(c >> AC_SHIFT);
+ if (ac < 0) // ignore transient negative
+ ac = 0;
String level;
if ((c & STOP_BIT) != 0)
level = (tc == 0) ? "Terminated" : "Terminating";
else
- level = shutdown ? "Shutting down" : "Running";
+ level = plock < 0 ? "Shutting down" : "Running";
return super.toString() +
"[" + level +
", parallelism = " + pc +
@@ -1898,11 +3050,13 @@
}
/**
- * Initiates an orderly shutdown in which previously submitted
- * tasks are executed, but no new tasks will be accepted.
- * Invocation has no additional effect if already shut down.
- * Tasks that are in the process of being submitted concurrently
- * during the course of this method may or may not be rejected.
+ * Possibly initiates an orderly shutdown in which previously
+ * submitted tasks are executed, but no new tasks will be
+ * accepted. Invocation has no effect on execution state if this
+ * is the {@link #commonPool}, and no additional effect if
+ * already shut down. Tasks that are in the process of being
+ * submitted concurrently during the course of this method may or
+ * may not be rejected.
*
* @throws SecurityException if a security manager exists and
* the caller is not permitted to modify threads
@@ -1911,19 +3065,20 @@
*/
public void shutdown() {
checkPermission();
- shutdown = true;
- tryTerminate(false);
+ tryTerminate(false, true);
}
/**
- * Attempts to cancel and/or stop all tasks, and reject all
- * subsequently submitted tasks. Tasks that are in the process of
- * being submitted or executed concurrently during the course of
- * this method may or may not be rejected. This method cancels
- * both existing and unexecuted tasks, in order to permit
- * termination in the presence of task dependencies. So the method
- * always returns an empty list (unlike the case for some other
- * Executors).
+ * Possibly attempts to cancel and/or stop all tasks, and reject
+ * all subsequently submitted tasks. Invocation has no effect on
+ * execution state if this is the {@link #commonPool}, and no
+ * additional effect if already shut down. Otherwise, tasks that
+ * are in the process of being submitted or executed concurrently
+ * during the course of this method may or may not be
+ * rejected. This method cancels both existing and unexecuted
+ * tasks, in order to permit termination in the presence of task
+ * dependencies. So the method always returns an empty list
+ * (unlike the case for some other Executors).
*
* @return an empty list
* @throws SecurityException if a security manager exists and
@@ -1933,8 +3088,7 @@
*/
public List<Runnable> shutdownNow() {
checkPermission();
- shutdown = true;
- tryTerminate(true);
+ tryTerminate(true, true);
return Collections.emptyList();
}
@@ -1946,7 +3100,7 @@
public boolean isTerminated() {
long c = ctl;
return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) == -parallelism);
+ (short)(c >>> TC_SHIFT) == -(config & SMASK));
}
/**
@@ -1954,7 +3108,7 @@
* commenced but not yet completed. This method may be useful for
* debugging. A return of {@code true} reported a sufficient
* period after shutdown may indicate that submitted tasks have
- * ignored or suppressed interruption, or are waiting for IO,
+ * ignored or suppressed interruption, or are waiting for I/O,
* causing this executor not to properly terminate. (See the
* advisory notes for class {@link ForkJoinTask} stating that
* tasks should not normally entail blocking operations. But if
@@ -1965,14 +3119,7 @@
public boolean isTerminating() {
long c = ctl;
return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) != -parallelism);
- }
-
- /**
- * Returns true if terminating or terminated. Used by ForkJoinWorkerThread.
- */
- final boolean isAtLeastTerminating() {
- return (ctl & STOP_BIT) != 0L;
+ (short)(c >>> TC_SHIFT) != -(config & SMASK));
}
/**
@@ -1981,13 +3128,15 @@
* @return {@code true} if this pool has been shut down
*/
public boolean isShutdown() {
- return shutdown;
+ return plock < 0;
}
/**
- * Blocks until all tasks have completed execution after a shutdown
- * request, or the timeout occurs, or the current thread is
- * interrupted, whichever happens first.
+ * Blocks until all tasks have completed execution after a
+ * shutdown request, or the timeout occurs, or the current thread
+ * is interrupted, whichever happens first. Note that the {@link
+ * #commonPool()} never terminates until program shutdown so
+ * this method will always time out.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
@@ -1998,19 +3147,21 @@
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.submissionLock;
- lock.lock();
- try {
- for (;;) {
- if (isTerminated())
- return true;
- if (nanos <= 0)
- return false;
- nanos = termination.awaitNanos(nanos);
+ if (isTerminated())
+ return true;
+ long startTime = System.nanoTime();
+ boolean terminated = false;
+ synchronized (this) {
+ for (long waitTime = nanos, millis = 0L;;) {
+ if (terminated = isTerminated() ||
+ waitTime <= 0L ||
+ (millis = unit.toMillis(waitTime)) <= 0L)
+ break;
+ wait(millis);
+ waitTime = nanos - (System.nanoTime() - startTime);
}
- } finally {
- lock.unlock();
}
+ return terminated;
}
/**
@@ -2110,11 +3261,35 @@
throws InterruptedException {
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread) {
- ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
- w.pool.awaitBlocker(blocker);
+ ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
+ while (!blocker.isReleasable()) { // variant of helpSignal
+ WorkQueue[] ws; WorkQueue q; int m, u;
+ if ((ws = p.workQueues) != null && (m = ws.length - 1) >= 0) {
+ for (int i = 0; i <= m; ++i) {
+ if (blocker.isReleasable())
+ return;
+ if ((q = ws[i]) != null && q.base - q.top < 0) {
+ p.signalWork(q);
+ if ((u = (int)(p.ctl >>> 32)) >= 0 ||
+ (u >> UAC_SHIFT) >= 0)
+ break;
+ }
+ }
+ }
+ if (p.tryCompensate()) {
+ try {
+ do {} while (!blocker.isReleasable() &&
+ !blocker.block());
+ } finally {
+ p.incrementActiveCount();
+ }
+ break;
+ }
+ }
}
else {
- do {} while (!blocker.isReleasable() && !blocker.block());
+ do {} while (!blocker.isReleasable() &&
+ !blocker.block());
}
}
@@ -2123,55 +3298,93 @@
// implement RunnableFuture.
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
- return (RunnableFuture<T>) ForkJoinTask.adapt(runnable, value);
+ return new ForkJoinTask.AdaptedRunnable<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
- return (RunnableFuture<T>) ForkJoinTask.adapt(callable);
+ return new ForkJoinTask.AdaptedCallable<T>(callable);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long ctlOffset;
- private static final long stealCountOffset;
- private static final long blockedCountOffset;
- private static final long quiescerCountOffset;
- private static final long scanGuardOffset;
- private static final long nextWorkerNumberOffset;
- private static final long ABASE;
+ private static final sun.misc.Unsafe U;
+ private static final long CTL;
+ private static final long PARKBLOCKER;
+ private static final int ABASE;
private static final int ASHIFT;
+ private static final long STEALCOUNT;
+ private static final long PLOCK;
+ private static final long INDEXSEED;
+ private static final long QLOCK;
static {
- poolNumberGenerator = new AtomicInteger();
- workerSeedGenerator = new Random();
- modifyThreadPermission = new RuntimePermission("modifyThread");
- defaultForkJoinWorkerThreadFactory =
- new DefaultForkJoinWorkerThreadFactory();
- int s;
+ int s; // initialize field offsets for CAS etc
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
+ U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ForkJoinPool.class;
- ctlOffset = UNSAFE.objectFieldOffset
+ CTL = U.objectFieldOffset
(k.getDeclaredField("ctl"));
- stealCountOffset = UNSAFE.objectFieldOffset
+ STEALCOUNT = U.objectFieldOffset
(k.getDeclaredField("stealCount"));
- blockedCountOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("blockedCount"));
- quiescerCountOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("quiescerCount"));
- scanGuardOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("scanGuard"));
- nextWorkerNumberOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("nextWorkerNumber"));
- Class<?> a = ForkJoinTask[].class;
- ABASE = UNSAFE.arrayBaseOffset(a);
- s = UNSAFE.arrayIndexScale(a);
+ PLOCK = U.objectFieldOffset
+ (k.getDeclaredField("plock"));
+ INDEXSEED = U.objectFieldOffset
+ (k.getDeclaredField("indexSeed"));
+ Class<?> tk = Thread.class;
+ PARKBLOCKER = U.objectFieldOffset
+ (tk.getDeclaredField("parkBlocker"));
+ Class<?> wk = WorkQueue.class;
+ QLOCK = U.objectFieldOffset
+ (wk.getDeclaredField("qlock"));
+ Class<?> ak = ForkJoinTask[].class;
+ ABASE = U.arrayBaseOffset(ak);
+ s = U.arrayIndexScale(ak);
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
} catch (Exception e) {
throw new Error(e);
}
if ((s & (s-1)) != 0)
throw new Error("data type scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
+
+ submitters = new ThreadLocal<Submitter>();
+ ForkJoinWorkerThreadFactory fac = defaultForkJoinWorkerThreadFactory =
+ new DefaultForkJoinWorkerThreadFactory();
+ modifyThreadPermission = new RuntimePermission("modifyThread");
+
+ /*
+ * Establish common pool parameters. For extra caution,
+ * computations to set up common pool state are here; the
+ * constructor just assigns these values to fields.
+ */
+
+ int par = 0;
+ Thread.UncaughtExceptionHandler handler = null;
+ try { // TBD: limit or report ignored exceptions?
+ String pp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.parallelism");
+ String hp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
+ String fp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.threadFactory");
+ if (fp != null)
+ fac = ((ForkJoinWorkerThreadFactory)ClassLoader.
+ getSystemClassLoader().loadClass(fp).newInstance());
+ if (hp != null)
+ handler = ((Thread.UncaughtExceptionHandler)ClassLoader.
+ getSystemClassLoader().loadClass(hp).newInstance());
+ if (pp != null)
+ par = Integer.parseInt(pp);
+ } catch (Exception ignore) {
+ }
+
+ if (par <= 0)
+ par = Runtime.getRuntime().availableProcessors();
+ if (par > MAX_CAP)
+ par = MAX_CAP;
+ commonPoolParallelism = par;
+ long np = (long)(-par); // precompute initial ctl value
+ long ct = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+
+ commonPool = new ForkJoinPool(par, ct, fac, handler);
}
}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinTask.java Wed Dec 26 10:08:36 2012 -0500
@@ -37,17 +37,13 @@
import java.io.Serializable;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.RandomAccess;
-import java.util.Map;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
@@ -63,46 +59,59 @@
* subtasks may be hosted by a small number of actual threads in a
* ForkJoinPool, at the price of some usage limitations.
*
- * <p>A "main" {@code ForkJoinTask} begins execution when submitted
- * to a {@link ForkJoinPool}. Once started, it will usually in turn
- * start other subtasks. As indicated by the name of this class,
- * many programs using {@code ForkJoinTask} employ only methods
- * {@link #fork} and {@link #join}, or derivatives such as {@link
+ * <p>A "main" {@code ForkJoinTask} begins execution when it is
+ * explicitly submitted to a {@link ForkJoinPool}, or, if not already
+ * engaged in a ForkJoin computation, commenced in the {@link
+ * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
+ * related methods. Once started, it will usually in turn start other
+ * subtasks. As indicated by the name of this class, many programs
+ * using {@code ForkJoinTask} employ only methods {@link #fork} and
+ * {@link #join}, or derivatives such as {@link
* #invokeAll(ForkJoinTask...) invokeAll}. However, this class also
* provides a number of other methods that can come into play in
- * advanced usages, as well as extension mechanics that allow
- * support of new forms of fork/join processing.
+ * advanced usages, as well as extension mechanics that allow support
+ * of new forms of fork/join processing.
*
* <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}.
* The efficiency of {@code ForkJoinTask}s stems from a set of
* restrictions (that are only partially statically enforceable)
- * reflecting their intended use as computational tasks calculating
- * pure functions or operating on purely isolated objects. The
- * primary coordination mechanisms are {@link #fork}, that arranges
+ * reflecting their main use as computational tasks calculating pure
+ * functions or operating on purely isolated objects. The primary
+ * coordination mechanisms are {@link #fork}, that arranges
* asynchronous execution, and {@link #join}, that doesn't proceed
* until the task's result has been computed. Computations should
- * avoid {@code synchronized} methods or blocks, and should minimize
- * other blocking synchronization apart from joining other tasks or
- * using synchronizers such as Phasers that are advertised to
- * cooperate with fork/join scheduling. Tasks should also not perform
- * blocking IO, and should ideally access variables that are
- * completely independent of those accessed by other running
- * tasks. Minor breaches of these restrictions, for example using
- * shared output streams, may be tolerable in practice, but frequent
- * use may result in poor performance, and the potential to
- * indefinitely stall if the number of threads not waiting for IO or
- * other external synchronization becomes exhausted. This usage
- * restriction is in part enforced by not permitting checked
- * exceptions such as {@code IOExceptions} to be thrown. However,
- * computations may still encounter unchecked exceptions, that are
- * rethrown to callers attempting to join them. These exceptions may
- * additionally include {@link RejectedExecutionException} stemming
- * from internal resource exhaustion, such as failure to allocate
- * internal task queues. Rethrown exceptions behave in the same way as
- * regular exceptions, but, when possible, contain stack traces (as
- * displayed for example using {@code ex.printStackTrace()}) of both
- * the thread that initiated the computation as well as the thread
- * actually encountering the exception; minimally only the latter.
+ * ideally avoid {@code synchronized} methods or blocks, and should
+ * minimize other blocking synchronization apart from joining other
+ * tasks or using synchronizers such as Phasers that are advertised to
+ * cooperate with fork/join scheduling. Subdividable tasks should also
+ * not perform blocking I/O, and should ideally access variables that
+ * are completely independent of those accessed by other running
+ * tasks. These guidelines are loosely enforced by not permitting
+ * checked exceptions such as {@code IOExceptions} to be
+ * thrown. However, computations may still encounter unchecked
+ * exceptions, that are rethrown to callers attempting to join
+ * them. These exceptions may additionally include {@link
+ * RejectedExecutionException} stemming from internal resource
+ * exhaustion, such as failure to allocate internal task
+ * queues. Rethrown exceptions behave in the same way as regular
+ * exceptions, but, when possible, contain stack traces (as displayed
+ * for example using {@code ex.printStackTrace()}) of both the thread
+ * that initiated the computation as well as the thread actually
+ * encountering the exception; minimally only the latter.
+ *
+ * <p>It is possible to define and use ForkJoinTasks that may block,
+ * but doing do requires three further considerations: (1) Completion
+ * of few if any <em>other</em> tasks should be dependent on a task
+ * that blocks on external synchronization or I/O. Event-style async
+ * tasks that are never joined (for example, those subclassing {@link
+ * CountedCompleter}) often fall into this category. (2) To minimize
+ * resource impact, tasks should be small; ideally performing only the
+ * (possibly) blocking action. (3) Unless the {@link
+ * ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
+ * blocked tasks is known to be less than the pool's {@link
+ * ForkJoinPool#getParallelism} level, the pool cannot guarantee that
+ * enough threads will be available to ensure progress or good
+ * performance.
*
* <p>The primary method for awaiting completion and extracting
* results of a task is {@link #join}, but there are several variants:
@@ -118,6 +127,13 @@
* performs the most common form of parallel invocation: forking a set
* of tasks and joining them all.
*
+ * <p>In the most typical usages, a fork-join pair act like a call
+ * (fork) and return (join) from a parallel recursive function. As is
+ * the case with other forms of recursive calls, returns (joins)
+ * should be performed innermost-first. For example, {@code a.fork();
+ * b.fork(); b.join(); a.join();} is likely to be substantially more
+ * efficient than joining {@code a} before {@code b}.
+ *
* <p>The execution status of tasks may be queried at several levels
* of detail: {@link #isDone} is true if a task completed in any way
* (including the case where a task was cancelled without executing);
@@ -133,18 +149,13 @@
* <p>The ForkJoinTask class is not usually directly subclassed.
* Instead, you subclass one of the abstract classes that support a
* particular style of fork/join processing, typically {@link
- * RecursiveAction} for computations that do not return results, or
- * {@link RecursiveTask} for those that do. Normally, a concrete
- * ForkJoinTask subclass declares fields comprising its parameters,
- * established in a constructor, and then defines a {@code compute}
- * method that somehow uses the control methods supplied by this base
- * class. While these methods have {@code public} access (to allow
- * instances of different task subclasses to call each other's
- * methods), some of them may only be called from within other
- * ForkJoinTasks (as may be determined using method {@link
- * #inForkJoinPool}). Attempts to invoke them in other contexts
- * result in exceptions or errors, possibly including
- * {@code ClassCastException}.
+ * RecursiveAction} for most computations that do not return results,
+ * {@link RecursiveTask} for those that do, and {@link
+ * CountedCompleter} for those in which completed actions trigger
+ * other actions. Normally, a concrete ForkJoinTask subclass declares
+ * fields comprising its parameters, established in a constructor, and
+ * then defines a {@code compute} method that somehow uses the control
+ * methods supplied by this base class.
*
* <p>Method {@link #join} and its variants are appropriate for use
* only when completion dependencies are acyclic; that is, the
@@ -154,7 +165,17 @@
* supports other methods and techniques (for example the use of
* {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that
* may be of use in constructing custom subclasses for problems that
- * are not statically structured as DAGs.
+ * are not statically structured as DAGs. To support such usages a
+ * ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
+ * value using {@link #setForkJoinTaskTag} or {@link
+ * #compareAndSetForkJoinTaskTag} and checked using {@link
+ * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
+ * these {@code protected} methods or tags for any purpose, but they
+ * may be of use in the construction of specialized subclasses. For
+ * example, parallel graph traversals can use the supplied methods to
+ * avoid revisiting nodes/tasks that have already been processed.
+ * (Method names for tagging are bulky in part to encourage definition
+ * of methods that reflect their usage patterns.)
*
* <p>Most base support methods are {@code final}, to prevent
* overriding of implementations that are intrinsically tied to the
@@ -194,41 +215,50 @@
* See the internal documentation of class ForkJoinPool for a
* general implementation overview. ForkJoinTasks are mainly
* responsible for maintaining their "status" field amidst relays
- * to methods in ForkJoinWorkerThread and ForkJoinPool. The
- * methods of this class are more-or-less layered into (1) basic
- * status maintenance (2) execution and awaiting completion (3)
- * user-level methods that additionally report results. This is
- * sometimes hard to see because this file orders exported methods
- * in a way that flows well in javadocs.
+ * to methods in ForkJoinWorkerThread and ForkJoinPool.
+ *
+ * The methods of this class are more-or-less layered into
+ * (1) basic status maintenance
+ * (2) execution and awaiting completion
+ * (3) user-level methods that additionally report results.
+ * This is sometimes hard to see because this file orders exported
+ * methods in a way that flows well in javadocs.
*/
/*
* The status field holds run control status bits packed into a
* single int to minimize footprint and to ensure atomicity (via
* CAS). Status is initially zero, and takes on nonnegative
- * values until completed, upon which status holds value
- * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking
- * waits by other threads have the SIGNAL bit set. Completion of
- * a stolen task with SIGNAL set awakens any waiters via
- * notifyAll. Even though suboptimal for some purposes, we use
- * basic builtin wait/notify to take advantage of "monitor
- * inflation" in JVMs that we would otherwise need to emulate to
- * avoid adding further per-task bookkeeping overhead. We want
- * these monitors to be "fat", i.e., not use biasing or thin-lock
- * techniques, so use some odd coding idioms that tend to avoid
- * them.
+ * values until completed, upon which status (anded with
+ * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
+ * undergoing blocking waits by other threads have the SIGNAL bit
+ * set. Completion of a stolen task with SIGNAL set awakens any
+ * waiters via notifyAll. Even though suboptimal for some
+ * purposes, we use basic builtin wait/notify to take advantage of
+ * "monitor inflation" in JVMs that we would otherwise need to
+ * emulate to avoid adding further per-task bookkeeping overhead.
+ * We want these monitors to be "fat", i.e., not use biasing or
+ * thin-lock techniques, so use some odd coding idioms that tend
+ * to avoid them, mainly by arranging that every synchronized
+ * block performs a wait, notifyAll or both.
+ *
+ * These control bits occupy only (some of) the upper half (16
+ * bits) of status field. The lower bits are used for user-defined
+ * tags.
*/
/** The run status of this task */
volatile int status; // accessed directly by pool and workers
- private static final int NORMAL = -1;
- private static final int CANCELLED = -2;
- private static final int EXCEPTIONAL = -3;
- private static final int SIGNAL = 1;
+ static final int DONE_MASK = 0xf0000000; // mask out non-completion bits
+ static final int NORMAL = 0xf0000000; // must be negative
+ static final int CANCELLED = 0xc0000000; // must be < NORMAL
+ static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED
+ static final int SIGNAL = 0x00010000; // must be >= 1 << 16
+ static final int SMASK = 0x0000ffff; // short bits for tags
/**
- * Marks completion and wakes up threads waiting to join this task,
- * also clearing signal request bits.
+ * Marks completion and wakes up threads waiting to join this
+ * task.
*
* @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
* @return completion status on exit
@@ -237,8 +267,8 @@
for (int s;;) {
if ((s = status) < 0)
return s;
- if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
- if (s != 0)
+ if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
+ if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
return completion;
}
@@ -246,27 +276,36 @@
}
/**
- * Tries to block a worker thread until completed or timed out.
- * Uses Object.wait time argument conventions.
- * May fail on contention or interrupt.
+ * Primary execution method for stolen tasks. Unless done, calls
+ * exec and records status if completed, but doesn't wait for
+ * completion otherwise.
*
- * @param millis if > 0, wait time.
+ * @return status on exit from this method
*/
- final void tryAwaitDone(long millis) {
- int s;
- try {
- if (((s = status) > 0 ||
- (s == 0 &&
- UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL))) &&
- status > 0) {
- synchronized (this) {
- if (status > 0)
- wait(millis);
- }
+ final int doExec() {
+ int s; boolean completed;
+ if ((s = status) >= 0) {
+ try {
+ completed = exec();
+ } catch (Throwable rex) {
+ return setExceptionalCompletion(rex);
}
- } catch (InterruptedException ie) {
- // caller must check termination
+ if (completed)
+ s = setCompletion(NORMAL);
}
+ return s;
+ }
+
+ /**
+ * Tries to set SIGNAL status unless already completed. Used by
+ * ForkJoinPool. Other variants are directly incorporated into
+ * externalAwaitDone etc.
+ *
+ * @return true if successful
+ */
+ final boolean trySetSignal() {
+ int s = status;
+ return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL);
}
/**
@@ -275,113 +314,78 @@
*/
private int externalAwaitDone() {
int s;
- if ((s = status) >= 0) {
- boolean interrupted = false;
- synchronized (this) {
- while ((s = status) >= 0) {
- if (s == 0)
- UNSAFE.compareAndSwapInt(this, statusOffset,
- 0, SIGNAL);
- else {
+ ForkJoinPool.externalHelpJoin(this);
+ boolean interrupted = false;
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
try {
wait();
} catch (InterruptedException ie) {
interrupted = true;
}
}
+ else
+ notifyAll();
}
}
- if (interrupted)
- Thread.currentThread().interrupt();
}
+ if (interrupted)
+ Thread.currentThread().interrupt();
return s;
}
/**
- * Blocks a non-worker-thread until completion or interruption or timeout.
+ * Blocks a non-worker-thread until completion or interruption.
*/
- private int externalInterruptibleAwaitDone(long millis)
- throws InterruptedException {
+ private int externalInterruptibleAwaitDone() throws InterruptedException {
int s;
if (Thread.interrupted())
throw new InterruptedException();
- if ((s = status) >= 0) {
- synchronized (this) {
- while ((s = status) >= 0) {
- if (s == 0)
- UNSAFE.compareAndSwapInt(this, statusOffset,
- 0, SIGNAL);
- else {
- wait(millis);
- if (millis > 0L)
- break;
- }
+ ForkJoinPool.externalHelpJoin(this);
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ wait();
+ else
+ notifyAll();
}
}
}
return s;
}
- /**
- * Primary execution method for stolen tasks. Unless done, calls
- * exec and records status if completed, but doesn't wait for
- * completion otherwise.
- */
- final void doExec() {
- if (status >= 0) {
- boolean completed;
- try {
- completed = exec();
- } catch (Throwable rex) {
- setExceptionalCompletion(rex);
- return;
- }
- if (completed)
- setCompletion(NORMAL); // must be outside try block
- }
- }
/**
- * Primary mechanics for join, get, quietlyJoin.
+ * Implementation for join, get, quietlyJoin. Directly handles
+ * only cases of already-completed, external wait, and
+ * unfork+exec. Others are relayed to ForkJoinPool.awaitJoin.
+ *
* @return status upon completion
*/
private int doJoin() {
- Thread t; ForkJoinWorkerThread w; int s; boolean completed;
- if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
- if ((s = status) < 0)
- return s;
- if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) {
- try {
- completed = exec();
- } catch (Throwable rex) {
- return setExceptionalCompletion(rex);
- }
- if (completed)
- return setCompletion(NORMAL);
- }
- return w.joinTask(this);
- }
- else
- return externalAwaitDone();
+ int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
+ return (s = status) < 0 ? s :
+ ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (w = (wt = (ForkJoinWorkerThread)t).workQueue).
+ tryUnpush(this) && (s = doExec()) < 0 ? s :
+ wt.pool.awaitJoin(w, this) :
+ externalAwaitDone();
}
/**
- * Primary mechanics for invoke, quietlyInvoke.
+ * Implementation for invoke, quietlyInvoke.
+ *
* @return status upon completion
*/
private int doInvoke() {
- int s; boolean completed;
- if ((s = status) < 0)
- return s;
- try {
- completed = exec();
- } catch (Throwable rex) {
- return setExceptionalCompletion(rex);
- }
- if (completed)
- return setCompletion(NORMAL);
- else
- return doJoin();
+ int s; Thread t; ForkJoinWorkerThread wt;
+ return (s = doExec()) < 0 ? s :
+ ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
+ externalAwaitDone();
}
// Exception table support
@@ -416,7 +420,7 @@
* any ForkJoinPool will call helpExpungeStaleExceptions when its
* pool becomes isQuiescent.
*/
- static final class ExceptionNode extends WeakReference<ForkJoinTask<?>>{
+ static final class ExceptionNode extends WeakReference<ForkJoinTask<?>> {
final Throwable ex;
ExceptionNode next;
final long thrower; // use id not ref to avoid weak cycles
@@ -429,30 +433,67 @@
}
/**
- * Records exception and sets exceptional completion.
+ * Records exception and sets status.
+ *
+ * @return status on exit
+ */
+ final int recordExceptionalCompletion(Throwable ex) {
+ int s;
+ if ((s = status) >= 0) {
+ int h = System.identityHashCode(this);
+ final ReentrantLock lock = exceptionTableLock;
+ lock.lock();
+ try {
+ expungeStaleExceptions();
+ ExceptionNode[] t = exceptionTable;
+ int i = h & (t.length - 1);
+ for (ExceptionNode e = t[i]; ; e = e.next) {
+ if (e == null) {
+ t[i] = new ExceptionNode(this, ex, t[i]);
+ break;
+ }
+ if (e.get() == this) // already present
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ s = setCompletion(EXCEPTIONAL);
+ }
+ return s;
+ }
+
+ /**
+ * Records exception and possibly propagates
*
* @return status on exit
*/
private int setExceptionalCompletion(Throwable ex) {
- int h = System.identityHashCode(this);
- final ReentrantLock lock = exceptionTableLock;
- lock.lock();
- try {
- expungeStaleExceptions();
- ExceptionNode[] t = exceptionTable;
- int i = h & (t.length - 1);
- for (ExceptionNode e = t[i]; ; e = e.next) {
- if (e == null) {
- t[i] = new ExceptionNode(this, ex, t[i]);
- break;
- }
- if (e.get() == this) // already present
- break;
+ int s = recordExceptionalCompletion(ex);
+ if ((s & DONE_MASK) == EXCEPTIONAL)
+ internalPropagateException(ex);
+ return s;
+ }
+
+ /**
+ * Hook for exception propagation support for tasks with completers.
+ */
+ void internalPropagateException(Throwable ex) {
+ }
+
+ /**
+ * Cancels, ignoring any exceptions thrown by cancel. Used during
+ * worker and pool shutdown. Cancel is spec'ed not to throw any
+ * exceptions, but if it does anyway, we have no recourse during
+ * shutdown, so guard against this case.
+ */
+ static final void cancelIgnoringExceptions(ForkJoinTask<?> t) {
+ if (t != null && t.status >= 0) {
+ try {
+ t.cancel(false);
+ } catch (Throwable ignore) {
}
- } finally {
- lock.unlock();
}
- return setCompletion(EXCEPTIONAL);
}
/**
@@ -501,7 +542,7 @@
* @return the exception, or null if none
*/
private Throwable getThrowableException() {
- if (status != EXCEPTIONAL)
+ if ((status & DONE_MASK) != EXCEPTIONAL)
return null;
int h = System.identityHashCode(this);
ExceptionNode e;
@@ -519,7 +560,7 @@
Throwable ex;
if (e == null || (ex = e.ex) == null)
return null;
- if (e.thrower != Thread.currentThread().getId()) {
+ if (false && e.thrower != Thread.currentThread().getId()) {
Class<? extends Throwable> ec = ex.getClass();
try {
Constructor<?> noArgCtor = null;
@@ -586,41 +627,61 @@
}
/**
- * Report the result of invoke or join; called only upon
- * non-normal return of internal versions.
+ * A version of "sneaky throw" to relay exceptions
*/
- private V reportResult() {
- int s; Throwable ex;
- if ((s = status) == CANCELLED)
+ static void rethrow(final Throwable ex) {
+ if (ex != null) {
+ if (ex instanceof Error)
+ throw (Error)ex;
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException)ex;
+ throw uncheckedThrowable(ex, RuntimeException.class);
+ }
+ }
+
+ /**
+ * The sneaky part of sneaky throw, relying on generics
+ * limitations to evade compiler complaints about rethrowing
+ * unchecked exceptions
+ */
+ @SuppressWarnings("unchecked") static <T extends Throwable>
+ T uncheckedThrowable(final Throwable t, final Class<T> c) {
+ return (T)t; // rely on vacuous cast
+ }
+
+ /**
+ * Throws exception, if any, associated with the given status.
+ */
+ private void reportException(int s) {
+ if (s == CANCELLED)
throw new CancellationException();
- if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
- UNSAFE.throwException(ex);
- return getRawResult();
+ if (s == EXCEPTIONAL)
+ rethrow(getThrowableException());
}
// public methods
/**
- * Arranges to asynchronously execute this task. While it is not
- * necessarily enforced, it is a usage error to fork a task more
- * than once unless it has completed and been reinitialized.
- * Subsequent modifications to the state of this task or any data
- * it operates on are not necessarily consistently observable by
- * any thread other than the one executing it unless preceded by a
- * call to {@link #join} or related methods, or a call to {@link
- * #isDone} returning {@code true}.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * Arranges to asynchronously execute this task in the pool the
+ * current task is running in, if applicable, or using the {@link
+ * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While
+ * it is not necessarily enforced, it is a usage error to fork a
+ * task more than once unless it has completed and been
+ * reinitialized. Subsequent modifications to the state of this
+ * task or any data it operates on are not necessarily
+ * consistently observable by any thread other than the one
+ * executing it unless preceded by a call to {@link #join} or
+ * related methods, or a call to {@link #isDone} returning {@code
+ * true}.
*
* @return {@code this}, to simplify usage
*/
public final ForkJoinTask<V> fork() {
- ((ForkJoinWorkerThread) Thread.currentThread())
- .pushTask(this);
+ Thread t;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ ((ForkJoinWorkerThread)t).workQueue.push(this);
+ else
+ ForkJoinPool.commonPool.externalPush(this);
return this;
}
@@ -636,10 +697,10 @@
* @return the computed result
*/
public final V join() {
- if (doJoin() != NORMAL)
- return reportResult();
- else
- return getRawResult();
+ int s;
+ if ((s = doJoin() & DONE_MASK) != NORMAL)
+ reportException(s);
+ return getRawResult();
}
/**
@@ -651,10 +712,10 @@
* @return the computed result
*/
public final V invoke() {
- if (doInvoke() != NORMAL)
- return reportResult();
- else
- return getRawResult();
+ int s;
+ if ((s = doInvoke() & DONE_MASK) != NORMAL)
+ reportException(s);
+ return getRawResult();
}
/**
@@ -670,20 +731,17 @@
* cancelled, completed normally or exceptionally, or left
* unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param t1 the first task
* @param t2 the second task
* @throws NullPointerException if any task is null
*/
public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
+ int s1, s2;
t2.fork();
- t1.invoke();
- t2.join();
+ if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL)
+ t1.reportException(s1);
+ if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL)
+ t2.reportException(s2);
}
/**
@@ -698,12 +756,6 @@
* related methods to check if they have been cancelled, completed
* normally or exceptionally, or left unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param tasks the tasks
* @throws NullPointerException if any task is null
*/
@@ -726,12 +778,12 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if (t.doJoin() < NORMAL && ex == null)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
if (ex != null)
- UNSAFE.throwException(ex);
+ rethrow(ex);
}
/**
@@ -747,12 +799,6 @@
* cancelled, completed normally or exceptionally, or left
* unprocessed.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @param tasks the collection of tasks
* @return the tasks argument, to simplify usage
* @throws NullPointerException if tasks or any element are null
@@ -783,12 +829,12 @@
if (t != null) {
if (ex != null)
t.cancel(false);
- else if (t.doJoin() < NORMAL && ex == null)
+ else if (t.doJoin() < NORMAL)
ex = t.getException();
}
}
if (ex != null)
- UNSAFE.throwException(ex);
+ rethrow(ex);
return tasks;
}
@@ -820,20 +866,7 @@
* @return {@code true} if this task is now cancelled
*/
public boolean cancel(boolean mayInterruptIfRunning) {
- return setCompletion(CANCELLED) == CANCELLED;
- }
-
- /**
- * Cancels, ignoring any exceptions thrown by cancel. Used during
- * worker and pool shutdown. Cancel is spec'ed not to throw any
- * exceptions, but if it does anyway, we have no recourse during
- * shutdown, so guard against this case.
- */
- final void cancelIgnoringExceptions() {
- try {
- cancel(false);
- } catch (Throwable ignore) {
- }
+ return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED;
}
public final boolean isDone() {
@@ -841,7 +874,7 @@
}
public final boolean isCancelled() {
- return status == CANCELLED;
+ return (status & DONE_MASK) == CANCELLED;
}
/**
@@ -861,7 +894,7 @@
* exception and was not cancelled
*/
public final boolean isCompletedNormally() {
- return status == NORMAL;
+ return (status & DONE_MASK) == NORMAL;
}
/**
@@ -872,7 +905,7 @@
* @return the exception, or {@code null} if none
*/
public final Throwable getException() {
- int s = status;
+ int s = status & DONE_MASK;
return ((s >= NORMAL) ? null :
(s == CANCELLED) ? new CancellationException() :
getThrowableException());
@@ -922,6 +955,18 @@
}
/**
+ * Completes this task normally without setting a value. The most
+ * recent value established by {@link #setRawResult} (or {@code
+ * null} by default) will be returned as the result of subsequent
+ * invocations of {@code join} and related operations.
+ *
+ * @since 1.8
+ */
+ public final void quietlyComplete() {
+ setCompletion(NORMAL);
+ }
+
+ /**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
@@ -934,9 +979,9 @@
*/
public final V get() throws InterruptedException, ExecutionException {
int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
- doJoin() : externalInterruptibleAwaitDone(0L);
+ doJoin() : externalInterruptibleAwaitDone();
Throwable ex;
- if (s == CANCELLED)
+ if ((s &= DONE_MASK) == CANCELLED)
throw new CancellationException();
if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
throw new ExecutionException(ex);
@@ -959,32 +1004,62 @@
*/
public final V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- Thread t = Thread.currentThread();
- if (t instanceof ForkJoinWorkerThread) {
- ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
- long nanos = unit.toNanos(timeout);
- if (status >= 0) {
- boolean completed = false;
- if (w.unpushTask(this)) {
- try {
- completed = exec();
- } catch (Throwable rex) {
- setExceptionalCompletion(rex);
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ // Messy in part because we measure in nanosecs, but wait in millisecs
+ int s; long ns, ms;
+ if ((s = status) >= 0 && (ns = unit.toNanos(timeout)) > 0L) {
+ long deadline = System.nanoTime() + ns;
+ ForkJoinPool p = null;
+ ForkJoinPool.WorkQueue w = null;
+ Thread t = Thread.currentThread();
+ if (t instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+ p = wt.pool;
+ w = wt.workQueue;
+ p.helpJoinOnce(w, this); // no retries on failure
+ }
+ else
+ ForkJoinPool.externalHelpJoin(this);
+ boolean canBlock = false;
+ boolean interrupted = false;
+ try {
+ while ((s = status) >= 0) {
+ if (w != null && w.qlock < 0)
+ cancelIgnoringExceptions(this);
+ else if (!canBlock) {
+ if (p == null || p.tryCompensate())
+ canBlock = true;
+ }
+ else {
+ if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
+ try {
+ wait(ms);
+ } catch (InterruptedException ie) {
+ if (p == null)
+ interrupted = true;
+ }
+ }
+ else
+ notifyAll();
+ }
+ }
+ if ((s = status) < 0 || interrupted ||
+ (ns = deadline - System.nanoTime()) <= 0L)
+ break;
}
}
- if (completed)
- setCompletion(NORMAL);
- else if (status >= 0 && nanos > 0)
- w.pool.timedAwaitJoin(this, nanos);
+ } finally {
+ if (p != null && canBlock)
+ p.incrementActiveCount();
}
+ if (interrupted)
+ throw new InterruptedException();
}
- else {
- long millis = unit.toMillis(timeout);
- if (millis > 0)
- externalInterruptibleAwaitDone(millis);
- }
- int s = status;
- if (s != NORMAL) {
+ if ((s &= DONE_MASK) != NORMAL) {
Throwable ex;
if (s == CANCELLED)
throw new CancellationException();
@@ -1021,16 +1096,15 @@
* be of use in designs in which many tasks are forked, but none
* are explicitly joined, instead executing them until all are
* processed.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
*/
public static void helpQuiesce() {
- ((ForkJoinWorkerThread) Thread.currentThread())
- .helpQuiescePool();
+ Thread t;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
+ wt.pool.helpQuiescePool(wt.workQueue);
+ }
+ else
+ ForkJoinPool.externalHelpQuiescePool();
}
/**
@@ -1050,7 +1124,7 @@
* setRawResult(null)}.
*/
public void reinitialize() {
- if (status == EXCEPTIONAL)
+ if ((status & DONE_MASK) == EXCEPTIONAL)
clearExceptionalCompletion();
else
status = 0;
@@ -1083,23 +1157,19 @@
/**
* Tries to unschedule this task for execution. This method will
- * typically succeed if this task is the most recently forked task
- * by the current thread, and has not commenced executing in
- * another thread. This method may be useful when arranging
- * alternative local processing of tasks that could have been, but
- * were not, stolen.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * typically (but is not guaranteed to) succeed if this task is
+ * the most recently forked task by the current thread, and has
+ * not commenced executing in another thread. This method may be
+ * useful when arranging alternative local processing of tasks
+ * that could have been, but were not, stolen.
*
* @return {@code true} if unforked
*/
public boolean tryUnfork() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .unpushTask(this);
+ Thread t;
+ return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread)t).workQueue.tryUnpush(this) :
+ ForkJoinPool.tryExternalUnpush(this));
}
/**
@@ -1108,40 +1178,32 @@
* value may be useful for heuristic decisions about whether to
* fork other tasks.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the number of tasks
*/
public static int getQueuedTaskCount() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .getQueueSize();
+ Thread t; ForkJoinPool.WorkQueue q;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ q = ((ForkJoinWorkerThread)t).workQueue;
+ else
+ q = ForkJoinPool.commonSubmitterQueue();
+ return (q == null) ? 0 : q.queueSize();
}
/**
* Returns an estimate of how many more locally queued tasks are
* held by the current worker thread than there are other worker
- * threads that might steal them. This value may be useful for
+ * threads that might steal them, or zero if this thread is not
+ * operating in a ForkJoinPool. This value may be useful for
* heuristic decisions about whether to fork other tasks. In many
* usages of ForkJoinTasks, at steady state, each worker should
* aim to maintain a small constant surplus (for example, 3) of
* tasks, and to process computations locally if this threshold is
* exceeded.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the surplus number of tasks, which may be negative
*/
public static int getSurplusQueuedTaskCount() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .getEstimatedSurplusTaskCount();
+ return ForkJoinPool.getSurplusQueuedTaskCount();
}
// Extension methods
@@ -1167,15 +1229,18 @@
protected abstract void setRawResult(V value);
/**
- * Immediately performs the base action of this task. This method
- * is designed to support extensions, and should not in general be
- * called otherwise. The return value controls whether this task
- * is considered to be done normally. It may return false in
+ * Immediately performs the base action of this task and returns
+ * true if, upon return from this method, this task is guaranteed
+ * to have completed normally. This method may return false
+ * otherwise, to indicate that this task is not necessarily
+ * complete (or is not known to be complete), for example in
* asynchronous actions that require explicit invocations of
- * {@link #complete} to become joinable. It may also throw an
- * (unchecked) exception to indicate abnormal exit.
+ * completion methods. This method may also throw an (unchecked)
+ * exception to indicate abnormal exit. This method is designed to
+ * support extensions, and should not in general be called
+ * otherwise.
*
- * @return {@code true} if completed normally
+ * @return {@code true} if this task is known to have completed normally
*/
protected abstract boolean exec();
@@ -1189,59 +1254,102 @@
* primarily to support extensions, and is unlikely to be useful
* otherwise.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return the next task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> peekNextLocalTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .peekTask();
+ Thread t; ForkJoinPool.WorkQueue q;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ q = ((ForkJoinWorkerThread)t).workQueue;
+ else
+ q = ForkJoinPool.commonSubmitterQueue();
+ return (q == null) ? null : q.peek();
}
/**
* Unschedules and returns, without executing, the next task
- * queued by the current thread but not yet executed. This method
- * is designed primarily to support extensions, and is unlikely to
- * be useful otherwise.
- *
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
+ * queued by the current thread but not yet executed, if the
+ * current thread is operating in a ForkJoinPool. This method is
+ * designed primarily to support extensions, and is unlikely to be
+ * useful otherwise.
*
* @return the next task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> pollNextLocalTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .pollLocalTask();
+ Thread t;
+ return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread)t).workQueue.nextLocalTask() :
+ null;
}
/**
- * Unschedules and returns, without executing, the next task
+ * If the current thread is operating in a ForkJoinPool,
+ * unschedules and returns, without executing, the next task
* queued by the current thread but not yet executed, if one is
* available, or if not available, a task that was forked by some
* other thread, if available. Availability may be transient, so a
- * {@code null} result does not necessarily imply quiescence
- * of the pool this task is operating in. This method is designed
+ * {@code null} result does not necessarily imply quiescence of
+ * the pool this task is operating in. This method is designed
* primarily to support extensions, and is unlikely to be useful
* otherwise.
*
- * <p>This method may be invoked only from within {@code
- * ForkJoinPool} computations (as may be determined using method
- * {@link #inForkJoinPool}). Attempts to invoke in other contexts
- * result in exceptions or errors, possibly including {@code
- * ClassCastException}.
- *
* @return a task, or {@code null} if none are available
*/
protected static ForkJoinTask<?> pollTask() {
- return ((ForkJoinWorkerThread) Thread.currentThread())
- .pollTask();
+ Thread t; ForkJoinWorkerThread wt;
+ return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ (wt = (ForkJoinWorkerThread)t).pool.nextTaskFor(wt.workQueue) :
+ null;
+ }
+
+ // tag operations
+
+ /**
+ * Returns the tag for this task.
+ *
+ * @return the tag for this task
+ * @since 1.8
+ */
+ public final short getForkJoinTaskTag() {
+ return (short)status;
+ }
+
+ /**
+ * Atomically sets the tag value for this task.
+ *
+ * @param tag the tag value
+ * @return the previous value of the tag
+ * @since 1.8
+ */
+ public final short setForkJoinTaskTag(short tag) {
+ for (int s;;) {
+ if (U.compareAndSwapInt(this, STATUS, s = status,
+ (s & ~SMASK) | (tag & SMASK)))
+ return (short)s;
+ }
+ }
+
+ /**
+ * Atomically conditionally sets the tag value for this task.
+ * Among other applications, tags can be used as visit markers
+ * in tasks operating on graphs, as in methods that check: {@code
+ * if (task.compareAndSetForkJoinTaskTag((short)0, (short)1))}
+ * before processing, otherwise exiting because the node has
+ * already been visited.
+ *
+ * @param e the expected tag value
+ * @param tag the new tag value
+ * @return true if successful; i.e., the current value was
+ * equal to e and is now tag.
+ * @since 1.8
+ */
+ public final boolean compareAndSetForkJoinTaskTag(short e, short tag) {
+ for (int s;;) {
+ if ((short)(s = status) != e)
+ return false;
+ if (U.compareAndSwapInt(this, STATUS, s,
+ (s & ~SMASK) | (tag & SMASK)))
+ return true;
+ }
}
/**
@@ -1252,21 +1360,33 @@
static final class AdaptedRunnable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
final Runnable runnable;
- final T resultOnCompletion;
T result;
AdaptedRunnable(Runnable runnable, T result) {
if (runnable == null) throw new NullPointerException();
this.runnable = runnable;
- this.resultOnCompletion = result;
+ this.result = result; // OK to set this even before completion
}
- public T getRawResult() { return result; }
- public void setRawResult(T v) { result = v; }
- public boolean exec() {
- runnable.run();
- result = resultOnCompletion;
- return true;
+ public final T getRawResult() { return result; }
+ public final void setRawResult(T v) { result = v; }
+ public final boolean exec() { runnable.run(); return true; }
+ public final void run() { invoke(); }
+ private static final long serialVersionUID = 5232453952276885070L;
+ }
+
+ /**
+ * Adaptor for Runnables without results
+ */
+ static final class AdaptedRunnableAction extends ForkJoinTask<Void>
+ implements RunnableFuture<Void> {
+ final Runnable runnable;
+ AdaptedRunnableAction(Runnable runnable) {
+ if (runnable == null) throw new NullPointerException();
+ this.runnable = runnable;
}
- public void run() { invoke(); }
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void v) { }
+ public final boolean exec() { runnable.run(); return true; }
+ public final void run() { invoke(); }
private static final long serialVersionUID = 5232453952276885070L;
}
@@ -1281,9 +1401,9 @@
if (callable == null) throw new NullPointerException();
this.callable = callable;
}
- public T getRawResult() { return result; }
- public void setRawResult(T v) { result = v; }
- public boolean exec() {
+ public final T getRawResult() { return result; }
+ public final void setRawResult(T v) { result = v; }
+ public final boolean exec() {
try {
result = callable.call();
return true;
@@ -1295,7 +1415,7 @@
throw new RuntimeException(ex);
}
}
- public void run() { invoke(); }
+ public final void run() { invoke(); }
private static final long serialVersionUID = 2838392045355241008L;
}
@@ -1308,7 +1428,7 @@
* @return the task
*/
public static ForkJoinTask<?> adapt(Runnable runnable) {
- return new AdaptedRunnable<Void>(runnable, null);
+ return new AdaptedRunnableAction(runnable);
}
/**
@@ -1342,11 +1462,10 @@
private static final long serialVersionUID = -7721805057305804111L;
/**
- * Saves the state to a stream (that is, serializes it).
+ * Saves this task to a stream (that is, serializes it).
*
* @serialData the current run status and the exception thrown
* during execution, or {@code null} if none
- * @param s the stream
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
@@ -1355,9 +1474,7 @@
}
/**
- * Reconstitutes the instance from a stream (that is, deserializes it).
- *
- * @param s the stream
+ * Reconstitutes this task from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1368,16 +1485,18 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long statusOffset;
+ private static final sun.misc.Unsafe U;
+ private static final long STATUS;
+
static {
exceptionTableLock = new ReentrantLock();
exceptionTableRefQueue = new ReferenceQueue<Object>();
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- statusOffset = UNSAFE.objectFieldOffset
- (ForkJoinTask.class.getDeclaredField("status"));
+ U = sun.misc.Unsafe.getUnsafe();
+ Class<?> k = ForkJoinTask.class;
+ STATUS = U.objectFieldOffset
+ (k.getDeclaredField("status"));
} catch (Exception e) {
throw new Error(e);
}
--- a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Dec 26 10:08:36 2012 -0500
@@ -35,9 +35,6 @@
package java.util.concurrent;
-import java.util.Collection;
-import java.util.concurrent.RejectedExecutionException;
-
/**
* A thread managed by a {@link ForkJoinPool}, which executes
* {@link ForkJoinTask}s.
@@ -54,238 +51,20 @@
*/
public class ForkJoinWorkerThread extends Thread {
/*
- * Overview:
- *
* ForkJoinWorkerThreads are managed by ForkJoinPools and perform
- * ForkJoinTasks. This class includes bookkeeping in support of
- * worker activation, suspension, and lifecycle control described
- * in more detail in the internal documentation of class
- * ForkJoinPool. And as described further below, this class also
- * includes special-cased support for some ForkJoinTask
- * methods. But the main mechanics involve work-stealing:
- *
- * Work-stealing queues are special forms of Deques that support
- * only three of the four possible end-operations -- push, pop,
- * and deq (aka steal), under the further constraints that push
- * and pop are called only from the owning thread, while deq may
- * be called from other threads. (If you are unfamiliar with
- * them, you probably want to read Herlihy and Shavit's book "The
- * Art of Multiprocessor programming", chapter 16 describing these
- * in more detail before proceeding.) The main work-stealing
- * queue design is roughly similar to those in the papers "Dynamic
- * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005
- * (http://research.sun.com/scalable/pubs/index.html) and
- * "Idempotent work stealing" by Michael, Saraswat, and Vechev,
- * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
- * The main differences ultimately stem from gc requirements that
- * we null out taken slots as soon as we can, to maintain as small
- * a footprint as possible even in programs generating huge
- * numbers of tasks. To accomplish this, we shift the CAS
- * arbitrating pop vs deq (steal) from being on the indices
- * ("queueBase" and "queueTop") to the slots themselves (mainly
- * via method "casSlotNull()"). So, both a successful pop and deq
- * mainly entail a CAS of a slot from non-null to null. Because
- * we rely on CASes of references, we do not need tag bits on
- * queueBase or queueTop. They are simple ints as used in any
- * circular array-based queue (see for example ArrayDeque).
- * Updates to the indices must still be ordered in a way that
- * guarantees that queueTop == queueBase means the queue is empty,
- * but otherwise may err on the side of possibly making the queue
- * appear nonempty when a push, pop, or deq have not fully
- * committed. Note that this means that the deq operation,
- * considered individually, is not wait-free. One thief cannot
- * successfully continue until another in-progress one (or, if
- * previously empty, a push) completes. However, in the
- * aggregate, we ensure at least probabilistic non-blockingness.
- * If an attempted steal fails, a thief always chooses a different
- * random victim target to try next. So, in order for one thief to
- * progress, it suffices for any in-progress deq or new push on
- * any empty queue to complete.
+ * ForkJoinTasks. For explanation, see the internal documentation
+ * of class ForkJoinPool.
*
- * This approach also enables support for "async mode" where local
- * task processing is in FIFO, not LIFO order; simply by using a
- * version of deq rather than pop when locallyFifo is true (as set
- * by the ForkJoinPool). This allows use in message-passing
- * frameworks in which tasks are never joined. However neither
- * mode considers affinities, loads, cache localities, etc, so
- * rarely provide the best possible performance on a given
- * machine, but portably provide good throughput by averaging over
- * these factors. (Further, even if we did try to use such
- * information, we do not usually have a basis for exploiting
- * it. For example, some sets of tasks profit from cache
- * affinities, but others are harmed by cache pollution effects.)
- *
- * When a worker would otherwise be blocked waiting to join a
- * task, it first tries a form of linear helping: Each worker
- * records (in field currentSteal) the most recent task it stole
- * from some other worker. Plus, it records (in field currentJoin)
- * the task it is currently actively joining. Method joinTask uses
- * these markers to try to find a worker to help (i.e., steal back
- * a task from and execute it) that could hasten completion of the
- * actively joined task. In essence, the joiner executes a task
- * that would be on its own local deque had the to-be-joined task
- * not been stolen. This may be seen as a conservative variant of
- * the approach in Wagner & Calder "Leapfrogging: a portable
- * technique for implementing efficient futures" SIGPLAN Notices,
- * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
- * in that: (1) We only maintain dependency links across workers
- * upon steals, rather than use per-task bookkeeping. This may
- * require a linear scan of workers array to locate stealers, but
- * usually doesn't because stealers leave hints (that may become
- * stale/wrong) of where to locate them. This isolates cost to
- * when it is needed, rather than adding to per-task overhead.
- * (2) It is "shallow", ignoring nesting and potentially cyclic
- * mutual steals. (3) It is intentionally racy: field currentJoin
- * is updated only while actively joining, which means that we
- * miss links in the chain during long-lived tasks, GC stalls etc
- * (which is OK since blocking in such cases is usually a good
- * idea). (4) We bound the number of attempts to find work (see
- * MAX_HELP) and fall back to suspending the worker and if
- * necessary replacing it with another.
- *
- * Efficient implementation of these algorithms currently relies
- * on an uncomfortable amount of "Unsafe" mechanics. To maintain
- * correct orderings, reads and writes of variable queueBase
- * require volatile ordering. Variable queueTop need not be
- * volatile because non-local reads always follow those of
- * queueBase. Similarly, because they are protected by volatile
- * queueBase reads, reads of the queue array and its slots by
- * other threads do not need volatile load semantics, but writes
- * (in push) require store order and CASes (in pop and deq)
- * require (volatile) CAS semantics. (Michael, Saraswat, and
- * Vechev's algorithm has similar properties, but without support
- * for nulling slots.) Since these combinations aren't supported
- * using ordinary volatiles, the only way to accomplish these
- * efficiently is to use direct Unsafe calls. (Using external
- * AtomicIntegers and AtomicReferenceArrays for the indices and
- * array is significantly slower because of memory locality and
- * indirection effects.)
- *
- * Further, performance on most platforms is very sensitive to
- * placement and sizing of the (resizable) queue array. Even
- * though these queues don't usually become all that big, the
- * initial size must be large enough to counteract cache
- * contention effects across multiple queues (especially in the
- * presence of GC cardmarking). Also, to improve thread-locality,
- * queues are initialized after starting.
+ * This class just maintains links to its pool and WorkQueue. The
+ * pool field is set immediately upon construction, but the
+ * workQueue field is not set until a call to registerWorker
+ * completes. This leads to a visibility race, that is tolerated
+ * by requiring that the workQueue field is only accessed by the
+ * owning thread.
*/
- /**
- * Mask for pool indices encoded as shorts
- */
- private static final int SMASK = 0xffff;
-
- /**
- * Capacity of work-stealing queue array upon initialization.
- * Must be a power of two. Initial size must be at least 4, but is
- * padded to minimize cache effects.
- */
- private static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
-
- /**
- * Maximum size for queue array. Must be a power of two
- * less than or equal to 1 << (31 - width of array entry) to
- * ensure lack of index wraparound, but is capped at a lower
- * value to help users trap runaway computations.
- */
- private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M
-
- /**
- * The work-stealing queue array. Size must be a power of two.
- * Initialized when started (as opposed to when constructed), to
- * improve memory locality.
- */
- ForkJoinTask<?>[] queue;
-
- /**
- * The pool this thread works in. Accessed directly by ForkJoinTask.
- */
- final ForkJoinPool pool;
-
- /**
- * Index (mod queue.length) of next queue slot to push to or pop
- * from. It is written only by owner thread, and accessed by other
- * threads only after reading (volatile) queueBase. Both queueTop
- * and queueBase are allowed to wrap around on overflow, but
- * (queueTop - queueBase) still estimates size.
- */
- int queueTop;
-
- /**
- * Index (mod queue.length) of least valid queue slot, which is
- * always the next position to steal from if nonempty.
- */
- volatile int queueBase;
-
- /**
- * The index of most recent stealer, used as a hint to avoid
- * traversal in method helpJoinTask. This is only a hint because a
- * worker might have had multiple steals and this only holds one
- * of them (usually the most current). Declared non-volatile,
- * relying on other prevailing sync to keep reasonably current.
- */
- int stealHint;
-
- /**
- * Index of this worker in pool array. Set once by pool before
- * running, and accessed directly by pool to locate this worker in
- * its workers array.
- */
- final int poolIndex;
-
- /**
- * Encoded record for pool task waits. Usages are always
- * surrounded by volatile reads/writes
- */
- int nextWait;
-
- /**
- * Complement of poolIndex, offset by count of entries of task
- * waits. Accessed by ForkJoinPool to manage event waiters.
- */
- volatile int eventCount;
-
- /**
- * Seed for random number generator for choosing steal victims.
- * Uses Marsaglia xorshift. Must be initialized as nonzero.
- */
- int seed;
-
- /**
- * Number of steals. Directly accessed (and reset) by pool when
- * idle.
- */
- int stealCount;
-
- /**
- * True if this worker should or did terminate
- */
- volatile boolean terminate;
-
- /**
- * Set to true before LockSupport.park; false on return
- */
- volatile boolean parked;
-
- /**
- * True if use local fifo, not default lifo, for local polling.
- * Shadows value from ForkJoinPool.
- */
- final boolean locallyFifo;
-
- /**
- * The task most recently stolen from another worker (or
- * submission queue). All uses are surrounded by enough volatile
- * reads/writes to maintain as non-volatile.
- */
- ForkJoinTask<?> currentSteal;
-
- /**
- * The task currently being joined, set only when actively trying
- * to help other stealers in helpJoinTask. All uses are surrounded
- * by enough volatile reads/writes to maintain as non-volatile.
- */
- ForkJoinTask<?> currentJoin;
+ final ForkJoinPool pool; // the pool this thread works in
+ final ForkJoinPool.WorkQueue workQueue; // work-stealing mechanics
/**
* Creates a ForkJoinWorkerThread operating in the given pool.
@@ -294,20 +73,12 @@
* @throws NullPointerException if pool is null
*/
protected ForkJoinWorkerThread(ForkJoinPool pool) {
- super(pool.nextWorkerName());
+ // Use a placeholder until a useful name can be set in registerWorker
+ super("aForkJoinWorkerThread");
this.pool = pool;
- int k = pool.registerWorker(this);
- poolIndex = k;
- eventCount = ~k & SMASK; // clear wait count
- locallyFifo = pool.locallyFifo;
- Thread.UncaughtExceptionHandler ueh = pool.ueh;
- if (ueh != null)
- setUncaughtExceptionHandler(ueh);
- setDaemon(true);
+ this.workQueue = pool.registerWorker(this);
}
- // Public methods
-
/**
* Returns the pool hosting this thread.
*
@@ -327,28 +98,9 @@
* @return the index number
*/
public int getPoolIndex() {
- return poolIndex;
+ return workQueue.poolIndex;
}
- // Randomization
-
- /**
- * Computes next value for random victim probes and backoffs.
- * Scans don't require a very high quality generator, but also not
- * a crummy one. Marsaglia xor-shift is cheap and works well
- * enough. Note: This is manually inlined in FJP.scan() to avoid
- * writes inside busy loops.
- */
- private int nextSeed() {
- int r = seed;
- r ^= r << 13;
- r ^= r >>> 17;
- r ^= r << 5;
- return seed = r;
- }
-
- // Run State management
-
/**
* Initializes internal state after construction but before
* processing any tasks. If you override this method, you must
@@ -359,9 +111,6 @@
* processing tasks.
*/
protected void onStart() {
- queue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY];
- int r = ForkJoinPool.workerSeedGenerator.nextInt();
- seed = (r == 0) ? 1 : r; // must be nonzero
}
/**
@@ -373,17 +122,6 @@
* to an unrecoverable error, or {@code null} if completed normally
*/
protected void onTermination(Throwable exception) {
- try {
- terminate = true;
- cancelTasks();
- pool.deregisterWorker(this, exception);
- } catch (Throwable ex) { // Shouldn't ever happen
- if (exception == null) // but if so, at least rethrown
- exception = ex;
- } finally {
- if (exception != null)
- UNSAFE.throwException(exception);
- }
}
/**
@@ -395,604 +133,18 @@
Throwable exception = null;
try {
onStart();
- pool.work(this);
+ pool.runWorker(workQueue);
} catch (Throwable ex) {
exception = ex;
} finally {
- onTermination(exception);
- }
- }
-
- /*
- * Intrinsics-based atomic writes for queue slots. These are
- * basically the same as methods in AtomicReferenceArray, but
- * specialized for (1) ForkJoinTask elements (2) requirement that
- * nullness and bounds checks have already been performed by
- * callers and (3) effective offsets are known not to overflow
- * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't
- * need corresponding version for reads: plain array reads are OK
- * because they are protected by other volatile reads and are
- * confirmed by CASes.
- *
- * Most uses don't actually call these methods, but instead
- * contain inlined forms that enable more predictable
- * optimization. We don't define the version of write used in
- * pushTask at all, but instead inline there a store-fenced array
- * slot write.
- *
- * Also in most methods, as a performance (not correctness) issue,
- * we'd like to encourage compilers not to arbitrarily postpone
- * setting queueTop after writing slot. Currently there is no
- * intrinsic for arranging this, but using Unsafe putOrderedInt
- * may be a preferable strategy on some compilers even though its
- * main effect is a pre-, not post- fence. To simplify possible
- * changes, the option is left in comments next to the associated
- * assignments.
- */
-
- /**
- * CASes slot i of array q from t to null. Caller must ensure q is
- * non-null and index is in range.
- */
- private static final boolean casSlotNull(ForkJoinTask<?>[] q, int i,
- ForkJoinTask<?> t) {
- return UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null);
- }
-
- /**
- * Performs a volatile write of the given task at given slot of
- * array q. Caller must ensure q is non-null and index is in
- * range. This method is used only during resets and backouts.
- */
- private static final void writeSlot(ForkJoinTask<?>[] q, int i,
- ForkJoinTask<?> t) {
- UNSAFE.putObjectVolatile(q, (i << ASHIFT) + ABASE, t);
- }
-
- // queue methods
-
- /**
- * Pushes a task. Call only from this thread.
- *
- * @param t the task. Caller must ensure non-null.
- */
- final void pushTask(ForkJoinTask<?> t) {
- ForkJoinTask<?>[] q; int s, m;
- if ((q = queue) != null) { // ignore if queue removed
- long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE;
- UNSAFE.putOrderedObject(q, u, t);
- queueTop = s + 1; // or use putOrderedInt
- if ((s -= queueBase) <= 2)
- pool.signalWork();
- else if (s == m)
- growQueue();
- }
- }
-
- /**
- * Creates or doubles queue array. Transfers elements by
- * emulating steals (deqs) from old array and placing, oldest
- * first, into new array.
- */
- private void growQueue() {
- ForkJoinTask<?>[] oldQ = queue;
- int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY;
- if (size > MAXIMUM_QUEUE_CAPACITY)
- throw new RejectedExecutionException("Queue capacity exceeded");
- if (size < INITIAL_QUEUE_CAPACITY)
- size = INITIAL_QUEUE_CAPACITY;
- ForkJoinTask<?>[] q = queue = new ForkJoinTask<?>[size];
- int mask = size - 1;
- int top = queueTop;
- int oldMask;
- if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) {
- for (int b = queueBase; b != top; ++b) {
- long u = ((b & oldMask) << ASHIFT) + ABASE;
- Object x = UNSAFE.getObjectVolatile(oldQ, u);
- if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null))
- UNSAFE.putObjectVolatile
- (q, ((b & mask) << ASHIFT) + ABASE, x);
+ try {
+ onTermination(exception);
+ } catch (Throwable ex) {
+ if (exception == null)
+ exception = ex;
+ } finally {
+ pool.deregisterWorker(this, exception);
}
}
}
-
- /**
- * Tries to take a task from the base of the queue, failing if
- * empty or contended. Note: Specializations of this code appear
- * in locallyDeqTask and elsewhere.
- *
- * @return a task, or null if none or contended
- */
- final ForkJoinTask<?> deqTask() {
- ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i;
- if (queueTop != (b = queueBase) &&
- (q = queue) != null && // must read q after b
- (i = (q.length - 1) & b) >= 0 &&
- (t = q[i]) != null && queueBase == b &&
- UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null)) {
- queueBase = b + 1;
- return t;
- }
- return null;
- }
-
- /**
- * Tries to take a task from the base of own queue. Called only
- * by this thread.
- *
- * @return a task, or null if none
- */
- final ForkJoinTask<?> locallyDeqTask() {
- ForkJoinTask<?> t; int m, b, i;
- ForkJoinTask<?>[] q = queue;
- if (q != null && (m = q.length - 1) >= 0) {
- while (queueTop != (b = queueBase)) {
- if ((t = q[i = m & b]) != null &&
- queueBase == b &&
- UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE,
- t, null)) {
- queueBase = b + 1;
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * Returns a popped task, or null if empty.
- * Called only by this thread.
- */
- private ForkJoinTask<?> popTask() {
- int m;
- ForkJoinTask<?>[] q = queue;
- if (q != null && (m = q.length - 1) >= 0) {
- for (int s; (s = queueTop) != queueBase;) {
- int i = m & --s;
- long u = (i << ASHIFT) + ABASE; // raw offset
- ForkJoinTask<?> t = q[i];
- if (t == null) // lost to stealer
- break;
- if (UNSAFE.compareAndSwapObject(q, u, t, null)) {
- queueTop = s; // or putOrderedInt
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * Specialized version of popTask to pop only if topmost element
- * is the given task. Called only by this thread.
- *
- * @param t the task. Caller must ensure non-null.
- */
- final boolean unpushTask(ForkJoinTask<?> t) {
- ForkJoinTask<?>[] q;
- int s;
- if ((q = queue) != null && (s = queueTop) != queueBase &&
- UNSAFE.compareAndSwapObject
- (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
- queueTop = s; // or putOrderedInt
- return true;
- }
- return false;
- }
-
- /**
- * Returns next task, or null if empty or contended.
- */
- final ForkJoinTask<?> peekTask() {
- int m;
- ForkJoinTask<?>[] q = queue;
- if (q == null || (m = q.length - 1) < 0)
- return null;
- int i = locallyFifo ? queueBase : (queueTop - 1);
- return q[i & m];
- }
-
- // Support methods for ForkJoinPool
-
- /**
- * Runs the given task, plus any local tasks until queue is empty
- */
- final void execTask(ForkJoinTask<?> t) {
- currentSteal = t;
- for (;;) {
- if (t != null)
- t.doExec();
- if (queueTop == queueBase)
- break;
- t = locallyFifo ? locallyDeqTask() : popTask();
- }
- ++stealCount;
- currentSteal = null;
- }
-
- /**
- * Removes and cancels all tasks in queue. Can be called from any
- * thread.
- */
- final void cancelTasks() {
- ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks
- if (cj != null && cj.status >= 0)
- cj.cancelIgnoringExceptions();
- ForkJoinTask<?> cs = currentSteal;
- if (cs != null && cs.status >= 0)
- cs.cancelIgnoringExceptions();
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = deqTask();
- if (t != null)
- t.cancelIgnoringExceptions();
- }
- }
-
- /**
- * Drains tasks to given collection c.
- *
- * @return the number of tasks drained
- */
- final int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
- int n = 0;
- while (queueBase != queueTop) {
- ForkJoinTask<?> t = deqTask();
- if (t != null) {
- c.add(t);
- ++n;
- }
- }
- return n;
- }
-
- // Support methods for ForkJoinTask
-
- /**
- * Returns an estimate of the number of tasks in the queue.
- */
- final int getQueueSize() {
- return queueTop - queueBase;
- }
-
- /**
- * Gets and removes a local task.
- *
- * @return a task, if available
- */
- final ForkJoinTask<?> pollLocalTask() {
- return locallyFifo ? locallyDeqTask() : popTask();
- }
-
- /**
- * Gets and removes a local or stolen task.
- *
- * @return a task, if available
- */
- final ForkJoinTask<?> pollTask() {
- ForkJoinWorkerThread[] ws;
- ForkJoinTask<?> t = pollLocalTask();
- if (t != null || (ws = pool.workers) == null)
- return t;
- int n = ws.length; // cheap version of FJP.scan
- int steps = n << 1;
- int r = nextSeed();
- int i = 0;
- while (i < steps) {
- ForkJoinWorkerThread w = ws[(i++ + r) & (n - 1)];
- if (w != null && w.queueBase != w.queueTop && w.queue != null) {
- if ((t = w.deqTask()) != null)
- return t;
- i = 0;
- }
- }
- return null;
- }
-
- /**
- * The maximum stolen->joining link depth allowed in helpJoinTask,
- * as well as the maximum number of retries (allowing on average
- * one staleness retry per level) per attempt to instead try
- * compensation. Depths for legitimate chains are unbounded, but
- * we use a fixed constant to avoid (otherwise unchecked) cycles
- * and bound staleness of traversal parameters at the expense of
- * sometimes blocking when we could be helping.
- */
- private static final int MAX_HELP = 16;
-
- /**
- * Possibly runs some tasks and/or blocks, until joinMe is done.
- *
- * @param joinMe the task to join
- * @return completion status on exit
- */
- final int joinTask(ForkJoinTask<?> joinMe) {
- ForkJoinTask<?> prevJoin = currentJoin;
- currentJoin = joinMe;
- for (int s, retries = MAX_HELP;;) {
- if ((s = joinMe.status) < 0) {
- currentJoin = prevJoin;
- return s;
- }
- if (retries > 0) {
- if (queueTop != queueBase) {
- if (!localHelpJoinTask(joinMe))
- retries = 0; // cannot help
- }
- else if (retries == MAX_HELP >>> 1) {
- --retries; // check uncommon case
- if (tryDeqAndExec(joinMe) >= 0)
- Thread.yield(); // for politeness
- }
- else
- retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1;
- }
- else {
- retries = MAX_HELP; // restart if not done
- pool.tryAwaitJoin(joinMe);
- }
- }
- }
-
- /**
- * If present, pops and executes the given task, or any other
- * cancelled task
- *
- * @return false if any other non-cancelled task exists in local queue
- */
- private boolean localHelpJoinTask(ForkJoinTask<?> joinMe) {
- int s, i; ForkJoinTask<?>[] q; ForkJoinTask<?> t;
- if ((s = queueTop) != queueBase && (q = queue) != null &&
- (i = (q.length - 1) & --s) >= 0 &&
- (t = q[i]) != null) {
- if (t != joinMe && t.status >= 0)
- return false;
- if (UNSAFE.compareAndSwapObject
- (q, (i << ASHIFT) + ABASE, t, null)) {
- queueTop = s; // or putOrderedInt
- t.doExec();
- }
- }
- return true;
- }
-
- /**
- * Tries to locate and execute tasks for a stealer of the given
- * task, or in turn one of its stealers, Traces
- * currentSteal->currentJoin links looking for a thread working on
- * a descendant of the given task and with a non-empty queue to
- * steal back and execute tasks from. The implementation is very
- * branchy to cope with potential inconsistencies or loops
- * encountering chains that are stale, unknown, or of length
- * greater than MAX_HELP links. All of these cases are dealt with
- * by just retrying by caller.
- *
- * @param joinMe the task to join
- * @param canSteal true if local queue is empty
- * @return true if ran a task
- */
- private boolean helpJoinTask(ForkJoinTask<?> joinMe) {
- boolean helped = false;
- int m = pool.scanGuard & SMASK;
- ForkJoinWorkerThread[] ws = pool.workers;
- if (ws != null && ws.length > m && joinMe.status >= 0) {
- int levels = MAX_HELP; // remaining chain length
- ForkJoinTask<?> task = joinMe; // base of chain
- outer:for (ForkJoinWorkerThread thread = this;;) {
- // Try to find v, the stealer of task, by first using hint
- ForkJoinWorkerThread v = ws[thread.stealHint & m];
- if (v == null || v.currentSteal != task) {
- for (int j = 0; ;) { // search array
- if ((v = ws[j]) != null && v.currentSteal == task) {
- thread.stealHint = j;
- break; // save hint for next time
- }
- if (++j > m)
- break outer; // can't find stealer
- }
- }
- // Try to help v, using specialized form of deqTask
- for (;;) {
- ForkJoinTask<?>[] q; int b, i;
- if (joinMe.status < 0)
- break outer;
- if ((b = v.queueBase) == v.queueTop ||
- (q = v.queue) == null ||
- (i = (q.length-1) & b) < 0)
- break; // empty
- long u = (i << ASHIFT) + ABASE;
- ForkJoinTask<?> t = q[i];
- if (task.status < 0)
- break outer; // stale
- if (t != null && v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- v.queueBase = b + 1;
- v.stealHint = poolIndex;
- ForkJoinTask<?> ps = currentSteal;
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- helped = true;
- }
- }
- // Try to descend to find v's stealer
- ForkJoinTask<?> next = v.currentJoin;
- if (--levels > 0 && task.status >= 0 &&
- next != null && next != task) {
- task = next;
- thread = v;
- }
- else
- break; // max levels, stale, dead-end, or cyclic
- }
- }
- return helped;
- }
-
- /**
- * Performs an uncommon case for joinTask: If task t is at base of
- * some workers queue, steals and executes it.
- *
- * @param t the task
- * @return t's status
- */
- private int tryDeqAndExec(ForkJoinTask<?> t) {
- int m = pool.scanGuard & SMASK;
- ForkJoinWorkerThread[] ws = pool.workers;
- if (ws != null && ws.length > m && t.status >= 0) {
- for (int j = 0; j <= m; ++j) {
- ForkJoinTask<?>[] q; int b, i;
- ForkJoinWorkerThread v = ws[j];
- if (v != null &&
- (b = v.queueBase) != v.queueTop &&
- (q = v.queue) != null &&
- (i = (q.length - 1) & b) >= 0 &&
- q[i] == t) {
- long u = (i << ASHIFT) + ABASE;
- if (v.queueBase == b &&
- UNSAFE.compareAndSwapObject(q, u, t, null)) {
- v.queueBase = b + 1;
- v.stealHint = poolIndex;
- ForkJoinTask<?> ps = currentSteal;
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- }
- break;
- }
- }
- }
- return t.status;
- }
-
- /**
- * Implements ForkJoinTask.getSurplusQueuedTaskCount(). Returns
- * an estimate of the number of tasks, offset by a function of
- * number of idle workers.
- *
- * This method provides a cheap heuristic guide for task
- * partitioning when programmers, frameworks, tools, or languages
- * have little or no idea about task granularity. In essence by
- * offering this method, we ask users only about tradeoffs in
- * overhead vs expected throughput and its variance, rather than
- * how finely to partition tasks.
- *
- * In a steady state strict (tree-structured) computation, each
- * thread makes available for stealing enough tasks for other
- * threads to remain active. Inductively, if all threads play by
- * the same rules, each thread should make available only a
- * constant number of tasks.
- *
- * The minimum useful constant is just 1. But using a value of 1
- * would require immediate replenishment upon each steal to
- * maintain enough tasks, which is infeasible. Further,
- * partitionings/granularities of offered tasks should minimize
- * steal rates, which in general means that threads nearer the top
- * of computation tree should generate more than those nearer the
- * bottom. In perfect steady state, each thread is at
- * approximately the same level of computation tree. However,
- * producing extra tasks amortizes the uncertainty of progress and
- * diffusion assumptions.
- *
- * So, users will want to use values larger, but not much larger
- * than 1 to both smooth over transient shortages and hedge
- * against uneven progress; as traded off against the cost of
- * extra task overhead. We leave the user to pick a threshold
- * value to compare with the results of this call to guide
- * decisions, but recommend values such as 3.
- *
- * When all threads are active, it is on average OK to estimate
- * surplus strictly locally. In steady-state, if one thread is
- * maintaining say 2 surplus tasks, then so are others. So we can
- * just use estimated queue length (although note that (queueTop -
- * queueBase) can be an overestimate because of stealers lagging
- * increments of queueBase). However, this strategy alone leads
- * to serious mis-estimates in some non-steady-state conditions
- * (ramp-up, ramp-down, other stalls). We can detect many of these
- * by further considering the number of "idle" threads, that are
- * known to have zero queued tasks, so compensate by a factor of
- * (#idle/#active) threads.
- */
- final int getEstimatedSurplusTaskCount() {
- return queueTop - queueBase - pool.idlePerActive();
- }
-
- /**
- * Runs tasks until {@code pool.isQuiescent()}. We piggyback on
- * pool's active count ctl maintenance, but rather than blocking
- * when tasks cannot be found, we rescan until all others cannot
- * find tasks either. The bracketing by pool quiescerCounts
- * updates suppresses pool auto-shutdown mechanics that could
- * otherwise prematurely terminate the pool because all threads
- * appear to be inactive.
- */
- final void helpQuiescePool() {
- boolean active = true;
- ForkJoinTask<?> ps = currentSteal; // to restore below
- ForkJoinPool p = pool;
- p.addQuiescerCount(1);
- for (;;) {
- ForkJoinWorkerThread[] ws = p.workers;
- ForkJoinWorkerThread v = null;
- int n;
- if (queueTop != queueBase)
- v = this;
- else if (ws != null && (n = ws.length) > 1) {
- ForkJoinWorkerThread w;
- int r = nextSeed(); // cheap version of FJP.scan
- int steps = n << 1;
- for (int i = 0; i < steps; ++i) {
- if ((w = ws[(i + r) & (n - 1)]) != null &&
- w.queueBase != w.queueTop) {
- v = w;
- break;
- }
- }
- }
- if (v != null) {
- ForkJoinTask<?> t;
- if (!active) {
- active = true;
- p.addActiveCount(1);
- }
- if ((t = (v != this) ? v.deqTask() :
- locallyFifo ? locallyDeqTask() : popTask()) != null) {
- currentSteal = t;
- t.doExec();
- currentSteal = ps;
- }
- }
- else {
- if (active) {
- active = false;
- p.addActiveCount(-1);
- }
- if (p.isQuiescent()) {
- p.addActiveCount(1);
- p.addQuiescerCount(-1);
- break;
- }
- }
- }
- }
-
- // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long ABASE;
- private static final int ASHIFT;
-
- static {
- int s;
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> a = ForkJoinTask[].class;
- ABASE = UNSAFE.arrayBaseOffset(a);
- s = UNSAFE.arrayIndexScale(a);
- } catch (Exception e) {
- throw new Error(e);
- }
- if ((s & (s-1)) != 0)
- throw new Error("data type scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(s);
- }
-
}
--- a/jdk/src/share/classes/javax/management/MBeanFeatureInfo.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/MBeanFeatureInfo.java Wed Dec 26 10:08:36 2012 -0500
@@ -239,12 +239,10 @@
case 1:
final String[] names = (String[])in.readObject();
- if (names.length == 0) {
- descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
- } else {
- final Object[] values = (Object[])in.readObject();
- descriptor = new ImmutableDescriptor(names, values);
- }
+ final Object[] values = (Object[]) in.readObject();
+ descriptor = (names.length == 0) ?
+ ImmutableDescriptor.EMPTY_DESCRIPTOR :
+ new ImmutableDescriptor(names, values);
break;
case 0:
--- a/jdk/src/share/classes/javax/management/MBeanInfo.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/MBeanInfo.java Wed Dec 26 10:08:36 2012 -0500
@@ -704,12 +704,10 @@
case 1:
final String[] names = (String[])in.readObject();
- if (names.length == 0) {
- descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
- } else {
- final Object[] values = (Object[])in.readObject();
- descriptor = new ImmutableDescriptor(names, values);
- }
+ final Object[] values = (Object[]) in.readObject();
+ descriptor = (names.length == 0) ?
+ ImmutableDescriptor.EMPTY_DESCRIPTOR :
+ new ImmutableDescriptor(names, values);
break;
case 0:
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorFactory.java Wed Dec 26 10:08:36 2012 -0500
@@ -137,8 +137,10 @@
* JAR conventions for service providers</a>, where the service
* interface is <code>JMXConnectorProvider</code>.</p>
*
- * <p>Every implementation must support the RMI connector protocols,
- * specified with the string <code>rmi</code> or
+ * <p>Every implementation must support the RMI connector protocol with
+ * the default RMI transport, specified with string <code>rmi</code>.
+ * An implementation may optionally support the RMI connector protocol
+ * with the RMI/IIOP transport, specified with the string
* <code>iiop</code>.</p>
*
* <p>Once a provider is found, the result of the
--- a/jdk/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Wed Dec 26 10:08:36 2012 -0500
@@ -129,8 +129,10 @@
* JAR conventions for service providers</a>, where the service
* interface is <code>JMXConnectorServerProvider</code>.</p>
*
- * <p>Every implementation must support the RMI connector protocols,
- * specified with the string <code>rmi</code> or
+ * <p>Every implementation must support the RMI connector protocol with
+ * the default RMI transport, specified with string <code>rmi</code>.
+ * An implementation may optionally support the RMI connector protocol
+ * with the RMI/IIOP transport, specified with the string
* <code>iiop</code>.</p>
*
* <p>Once a provider is found, the result of the
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnector.java Wed Dec 26 10:08:36 2012 -0500
@@ -238,10 +238,21 @@
//--------------------------------------------------------------------
// implements JMXConnector interface
//--------------------------------------------------------------------
+
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem, or in the case of the {@code iiop} protocol,
+ * that RMI/IIOP is not supported
+ */
public void connect() throws IOException {
connect(null);
}
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem, or in the case of the {@code iiop} protocol,
+ * that RMI/IIOP is not supported
+ */
public synchronized void connect(Map<String,?> environment)
throws IOException {
final boolean tracing = logger.traceOn();
--- a/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Wed Dec 26 10:08:36 2012 -0500
@@ -337,7 +337,8 @@
* @exception IllegalStateException if the connector server has
* not been attached to an MBean server.
* @exception IOException if the connector server cannot be
- * started.
+ * started, or in the case of the {@code iiop} protocol, that
+ * RMI/IIOP is not supported.
*/
public synchronized void start() throws IOException {
final boolean tracing = logger.traceOn();
--- a/jdk/src/share/classes/javax/management/remote/rmi/package.html Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/javax/management/remote/rmi/package.html Wed Dec 26 10:08:36 2012 -0500
@@ -36,8 +36,8 @@
that different implementations of the RMI connector can
interoperate.</p>
- <p>The RMI connector supports both the JRMP and the IIOP transports
- for RMI.</p>
+ <p>The RMI connector supports the JRMP transport for RMI, and
+ optionally the IIOP transport.</p>
<p>Like most connectors in the JMX Remote API, an RMI connector
usually has an address, which
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/Attributes.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// Attributes.java - attribute list with Namespace support
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: Attributes.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Interface for a list of XML attributes.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This interface allows access to a list of attributes in
+ * three different ways:</p>
+ *
+ * <ol>
+ * <li>by attribute index;</li>
+ * <li>by Namespace-qualified name; or</li>
+ * <li>by qualified (prefixed) name.</li>
+ * </ol>
+ *
+ * <p>The list will not contain attributes that were declared
+ * #IMPLIED but not specified in the start tag. It will also not
+ * contain attributes used as Namespace declarations (xmlns*) unless
+ * the <code>http://xml.org/sax/features/namespace-prefixes</code>
+ * feature is set to <var>true</var> (it is <var>false</var> by
+ * default).
+ * Because SAX2 conforms to the original "Namespaces in XML"
+ * recommendation, it normally does not
+ * give namespace declaration attributes a namespace URI.
+ * </p>
+ *
+ * <p>Some SAX2 parsers may support using an optional feature flag
+ * (<code>http://xml.org/sax/features/xmlns-uris</code>) to request
+ * that those attributes be given URIs, conforming to a later
+ * backwards-incompatible revision of that recommendation. (The
+ * attribute's "local name" will be the prefix, or "xmlns" when
+ * defining a default element namespace.) For portability, handler
+ * code should always resolve that conflict, rather than requiring
+ * parsers that can change the setting of that feature flag. </p>
+ *
+ * <p>If the namespace-prefixes feature (see above) is
+ * <var>false</var>, access by qualified name may not be available; if
+ * the <code>http://xml.org/sax/features/namespaces</code> feature is
+ * <var>false</var>, access by Namespace-qualified names may not be
+ * available.</p>
+ *
+ * <p>This interface replaces the now-deprecated SAX1 {@link
+ * org.xml.sax.AttributeList AttributeList} interface, which does not
+ * contain Namespace support. In addition to Namespace support, it
+ * adds the <var>getIndex</var> methods (below).</p>
+ *
+ * <p>The order of attributes in the list is unspecified, and will
+ * vary from implementation to implementation.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.helpers.AttributesImpl
+ * @see org.xml.sax.ext.DeclHandler#attributeDecl
+ */
+public interface Attributes
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Indexed access.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * <p>Once you know the number of attributes, you can iterate
+ * through the list.</p>
+ *
+ * @return The number of attributes in the list.
+ * @see #getURI(int)
+ * @see #getLocalName(int)
+ * @see #getQName(int)
+ * @see #getType(int)
+ * @see #getValue(int)
+ */
+ public abstract int getLength ();
+
+
+ /**
+ * Look up an attribute's Namespace URI by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The Namespace URI, or the empty string if none
+ * is available, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public abstract String getURI (int index);
+
+
+ /**
+ * Look up an attribute's local name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The local name, or the empty string if Namespace
+ * processing is not being performed, or null
+ * if the index is out of range.
+ * @see #getLength
+ */
+ public abstract String getLocalName (int index);
+
+
+ /**
+ * Look up an attribute's XML qualified (prefixed) name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The XML qualified name, or the empty string
+ * if none is available, or null if the index
+ * is out of range.
+ * @see #getLength
+ */
+ public abstract String getQName (int index);
+
+
+ /**
+ * Look up an attribute's type by index.
+ *
+ * <p>The attribute type is one of the strings "CDATA", "ID",
+ * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
+ * or "NOTATION" (always in upper case).</p>
+ *
+ * <p>If the parser has not read a declaration for the attribute,
+ * or if the parser does not report attribute types, then it must
+ * return the value "CDATA" as stated in the XML 1.0 Recommendation
+ * (clause 3.3.3, "Attribute-Value Normalization").</p>
+ *
+ * <p>For an enumerated attribute that is not a notation, the
+ * parser will report the type as "NMTOKEN".</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's type as a string, or null if the
+ * index is out of range.
+ * @see #getLength
+ */
+ public abstract String getType (int index);
+
+
+ /**
+ * Look up an attribute's value by index.
+ *
+ * <p>If the attribute value is a list of tokens (IDREFS,
+ * ENTITIES, or NMTOKENS), the tokens will be concatenated
+ * into a single string with each token separated by a
+ * single space.</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's value as a string, or null if the
+ * index is out of range.
+ * @see #getLength
+ */
+ public abstract String getValue (int index);
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Name-based query.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if
+ * the name has no Namespace URI.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not
+ * appear in the list.
+ */
+ public int getIndex (String uri, String localName);
+
+
+ /**
+ * Look up the index of an attribute by XML qualified (prefixed) name.
+ *
+ * @param qName The qualified (prefixed) name.
+ * @return The index of the attribute, or -1 if it does not
+ * appear in the list.
+ */
+ public int getIndex (String qName);
+
+
+ /**
+ * Look up an attribute's type by Namespace name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description
+ * of the possible types.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the
+ * name has no Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute type as a string, or null if the
+ * attribute is not in the list or if Namespace
+ * processing is not being performed.
+ */
+ public abstract String getType (String uri, String localName);
+
+
+ /**
+ * Look up an attribute's type by XML qualified (prefixed) name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description
+ * of the possible types.</p>
+ *
+ * @param qName The XML qualified name.
+ * @return The attribute type as a string, or null if the
+ * attribute is not in the list or if qualified names
+ * are not available.
+ */
+ public abstract String getType (String qName);
+
+
+ /**
+ * Look up an attribute's value by Namespace name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description
+ * of the possible values.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the
+ * name has no Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute value as a string, or null if the
+ * attribute is not in the list.
+ */
+ public abstract String getValue (String uri, String localName);
+
+
+ /**
+ * Look up an attribute's value by XML qualified (prefixed) name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description
+ * of the possible values.</p>
+ *
+ * @param qName The XML qualified name.
+ * @return The attribute value as a string, or null if the
+ * attribute is not in the list or if qualified names
+ * are not available.
+ */
+ public abstract String getValue (String qName);
+
+}
+
+// end of Attributes.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/ContentHandler.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// ContentHandler.java - handle main document content.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: ContentHandler.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Receive notification of the logical content of a document.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This is the main interface that most SAX applications
+ * implement: if the application needs to be informed of basic parsing
+ * events, it implements this interface and registers an instance with
+ * the SAX parser using the {@link org.xml.sax.XMLReader#setContentHandler
+ * setContentHandler} method. The parser uses the instance to report
+ * basic document-related events like the start and end of elements
+ * and character data.</p>
+ *
+ * <p>The order of events in this interface is very important, and
+ * mirrors the order of information in the document itself. For
+ * example, all of an element's content (character data, processing
+ * instructions, and/or subelements) will appear, in order, between
+ * the startElement event and the corresponding endElement event.</p>
+ *
+ * <p>This interface is similar to the now-deprecated SAX 1.0
+ * DocumentHandler interface, but it adds support for Namespaces
+ * and for reporting skipped entities (in non-validating XML
+ * processors).</p>
+ *
+ * <p>Implementors should note that there is also a
+ * <code>ContentHandler</code> class in the <code>java.net</code>
+ * package; that means that it's probably a bad idea to do</p>
+ *
+ * <pre>import java.net.*;
+ * import org.xml.sax.*;
+ * </pre>
+ *
+ * <p>In fact, "import ...*" is usually a sign of sloppy programming
+ * anyway, so the user should consider this a feature rather than a
+ * bug.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader
+ * @see org.xml.sax.DTDHandler
+ * @see org.xml.sax.ErrorHandler
+ */
+public interface ContentHandler
+{
+
+ /**
+ * Receive an object for locating the origin of SAX document events.
+ *
+ * <p>SAX parsers are strongly encouraged (though not absolutely
+ * required) to supply a locator: if it does so, it must supply
+ * the locator to the application by invoking this method before
+ * invoking any of the other methods in the ContentHandler
+ * interface.</p>
+ *
+ * <p>The locator allows the application to determine the end
+ * position of any document-related event, even if the parser is
+ * not reporting an error. Typically, the application will
+ * use this information for reporting its own errors (such as
+ * character content that does not match an application's
+ * business rules). The information returned by the locator
+ * is probably not sufficient for use with a search engine.</p>
+ *
+ * <p>Note that the locator will return correct information only
+ * during the invocation SAX event callbacks after
+ * {@link #startDocument startDocument} returns and before
+ * {@link #endDocument endDocument} is called. The
+ * application should not attempt to use it at any other time.</p>
+ *
+ * @param locator an object that can return the location of
+ * any SAX document event
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator (Locator locator);
+
+
+ /**
+ * Receive notification of the beginning of a document.
+ *
+ * <p>The SAX parser will invoke this method only once, before any
+ * other event callbacks (except for {@link #setDocumentLocator
+ * setDocumentLocator}).</p>
+ *
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #endDocument
+ */
+ public void startDocument ()
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the end of a document.
+ *
+ * <p><strong>There is an apparent contradiction between the
+ * documentation for this method and the documentation for {@link
+ * org.xml.sax.ErrorHandler#fatalError}. Until this ambiguity is
+ * resolved in a future major release, clients should make no
+ * assumptions about whether endDocument() will or will not be
+ * invoked when the parser has reported a fatalError() or thrown
+ * an exception.</strong></p>
+ *
+ * <p>The SAX parser will invoke this method only once, and it will
+ * be the last method invoked during the parse. The parser shall
+ * not invoke this method until it has either abandoned parsing
+ * (because of an unrecoverable error) or reached the end of
+ * input.</p>
+ *
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #startDocument
+ */
+ public void endDocument()
+ throws SAXException;
+
+
+ /**
+ * Begin the scope of a prefix-URI Namespace mapping.
+ *
+ * <p>The information from this event is not necessary for
+ * normal Namespace processing: the SAX XML reader will
+ * automatically replace prefixes for element and attribute
+ * names when the <code>http://xml.org/sax/features/namespaces</code>
+ * feature is <var>true</var> (the default).</p>
+ *
+ * <p>There are cases, however, when applications need to
+ * use prefixes in character data or in attribute values,
+ * where they cannot safely be expanded automatically; the
+ * start/endPrefixMapping event supplies the information
+ * to the application to expand prefixes in those contexts
+ * itself, if necessary.</p>
+ *
+ * <p>Note that start/endPrefixMapping events are not
+ * guaranteed to be properly nested relative to each other:
+ * all startPrefixMapping events will occur immediately before the
+ * corresponding {@link #startElement startElement} event,
+ * and all {@link #endPrefixMapping endPrefixMapping}
+ * events will occur immediately after the corresponding
+ * {@link #endElement endElement} event,
+ * but their order is not otherwise
+ * guaranteed.</p>
+ *
+ * <p>There should never be start/endPrefixMapping events for the
+ * "xml" prefix, since it is predeclared and immutable.</p>
+ *
+ * @param prefix the Namespace prefix being declared.
+ * An empty string is used for the default element namespace,
+ * which has no prefix.
+ * @param uri the Namespace URI the prefix is mapped to
+ * @throws org.xml.sax.SAXException the client may throw
+ * an exception during processing
+ * @see #endPrefixMapping
+ * @see #startElement
+ */
+ public void startPrefixMapping (String prefix, String uri)
+ throws SAXException;
+
+
+ /**
+ * End the scope of a prefix-URI mapping.
+ *
+ * <p>See {@link #startPrefixMapping startPrefixMapping} for
+ * details. These events will always occur immediately after the
+ * corresponding {@link #endElement endElement} event, but the order of
+ * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
+ * guaranteed.</p>
+ *
+ * @param prefix the prefix that was being mapped.
+ * This is the empty string when a default mapping scope ends.
+ * @throws org.xml.sax.SAXException the client may throw
+ * an exception during processing
+ * @see #startPrefixMapping
+ * @see #endElement
+ */
+ public void endPrefixMapping (String prefix)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the beginning of an element.
+ *
+ * <p>The Parser will invoke this method at the beginning of every
+ * element in the XML document; there will be a corresponding
+ * {@link #endElement endElement} event for every startElement event
+ * (even when the element is empty). All of the element's content will be
+ * reported, in order, before the corresponding endElement
+ * event.</p>
+ *
+ * <p>This event allows up to three name components for each
+ * element:</p>
+ *
+ * <ol>
+ * <li>the Namespace URI;</li>
+ * <li>the local name; and</li>
+ * <li>the qualified (prefixed) name.</li>
+ * </ol>
+ *
+ * <p>Any or all of these may be provided, depending on the
+ * values of the <var>http://xml.org/sax/features/namespaces</var>
+ * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
+ * properties:</p>
+ *
+ * <ul>
+ * <li>the Namespace URI and local name are required when
+ * the namespaces property is <var>true</var> (the default), and are
+ * optional when the namespaces property is <var>false</var> (if one is
+ * specified, both must be);</li>
+ * <li>the qualified name is required when the namespace-prefixes property
+ * is <var>true</var>, and is optional when the namespace-prefixes property
+ * is <var>false</var> (the default).</li>
+ * </ul>
+ *
+ * <p>Note that the attribute list provided will contain only
+ * attributes with explicit values (specified or defaulted):
+ * #IMPLIED attributes will be omitted. The attribute list
+ * will contain attributes used for Namespace declarations
+ * (xmlns* attributes) only if the
+ * <code>http://xml.org/sax/features/namespace-prefixes</code>
+ * property is true (it is false by default, and support for a
+ * true value is optional).</p>
+ *
+ * <p>Like {@link #characters characters()}, attribute values may have
+ * characters that need more than one <code>char</code> value. </p>
+ *
+ * @param uri the Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed
+ * @param qName the qualified name (with prefix), or the
+ * empty string if qualified names are not available
+ * @param atts the attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object. The value of this object after
+ * startElement returns is undefined
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #endElement
+ * @see org.xml.sax.Attributes
+ * @see org.xml.sax.helpers.AttributesImpl
+ */
+ public void startElement (String uri, String localName,
+ String qName, Attributes atts)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of the end of an element.
+ *
+ * <p>The SAX parser will invoke this method at the end of every
+ * element in the XML document; there will be a corresponding
+ * {@link #startElement startElement} event for every endElement
+ * event (even when the element is empty).</p>
+ *
+ * <p>For information on the names, see startElement.</p>
+ *
+ * @param uri the Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed
+ * @param localName the local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed
+ * @param qName the qualified XML name (with prefix), or the
+ * empty string if qualified names are not available
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void endElement (String uri, String localName,
+ String qName)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of character data.
+ *
+ * <p>The Parser will call this method to report each chunk of
+ * character data. SAX parsers may return all contiguous character
+ * data in a single chunk, or they may split it into several
+ * chunks; however, all of the characters in any single event
+ * must come from the same external entity so that the Locator
+ * provides useful information.</p>
+ *
+ * <p>The application must not attempt to read from the array
+ * outside of the specified range.</p>
+ *
+ * <p>Individual characters may consist of more than one Java
+ * <code>char</code> value. There are two important cases where this
+ * happens, because characters can't be represented in just sixteen bits.
+ * In one case, characters are represented in a <em>Surrogate Pair</em>,
+ * using two special Unicode values. Such characters are in the so-called
+ * "Astral Planes", with a code point above U+FFFF. A second case involves
+ * composite characters, such as a base character combining with one or
+ * more accent characters. </p>
+ *
+ * <p> Your code should not assume that algorithms using
+ * <code>char</code>-at-a-time idioms will be working in character
+ * units; in some cases they will split characters. This is relevant
+ * wherever XML permits arbitrary characters, such as attribute values,
+ * processing instruction data, and comments as well as in data reported
+ * from this method. It's also generally relevant whenever Java code
+ * manipulates internationalized text; the issue isn't unique to XML.</p>
+ *
+ * <p>Note that some parsers will report whitespace in element
+ * content using the {@link #ignorableWhitespace ignorableWhitespace}
+ * method rather than this one (validating parsers <em>must</em>
+ * do so).</p>
+ *
+ * @param ch the characters from the XML document
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #ignorableWhitespace
+ * @see org.xml.sax.Locator
+ */
+ public void characters (char ch[], int start, int length)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of ignorable whitespace in element content.
+ *
+ * <p>Validating Parsers must use this method to report each chunk
+ * of whitespace in element content (see the W3C XML 1.0
+ * recommendation, section 2.10): non-validating parsers may also
+ * use this method if they are capable of parsing and using
+ * content models.</p>
+ *
+ * <p>SAX parsers may return all contiguous whitespace in a single
+ * chunk, or they may split it into several chunks; however, all of
+ * the characters in any single event must come from the same
+ * external entity, so that the Locator provides useful
+ * information.</p>
+ *
+ * <p>The application must not attempt to read from the array
+ * outside of the specified range.</p>
+ *
+ * @param ch the characters from the XML document
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ * @see #characters
+ */
+ public void ignorableWhitespace (char ch[], int start, int length)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a processing instruction.
+ *
+ * <p>The Parser will invoke this method once for each processing
+ * instruction found: note that processing instructions may occur
+ * before or after the main document element.</p>
+ *
+ * <p>A SAX parser must never report an XML declaration (XML 1.0,
+ * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
+ * using this method.</p>
+ *
+ * <p>Like {@link #characters characters()}, processing instruction
+ * data may have characters that need more than one <code>char</code>
+ * value. </p>
+ *
+ * @param target the processing instruction target
+ * @param data the processing instruction data, or null if
+ * none was supplied. The data does not include any
+ * whitespace separating it from the target
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void processingInstruction (String target, String data)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a skipped entity.
+ * This is not called for entity references within markup constructs
+ * such as element start tags or markup declarations. (The XML
+ * recommendation requires reporting skipped external entities.
+ * SAX also reports internal entity expansion/non-expansion, except
+ * within markup constructs.)
+ *
+ * <p>The Parser will invoke this method each time the entity is
+ * skipped. Non-validating processors may skip entities if they
+ * have not seen the declarations (because, for example, the
+ * entity was declared in an external DTD subset). All processors
+ * may skip external entities, depending on the values of the
+ * <code>http://xml.org/sax/features/external-general-entities</code>
+ * and the
+ * <code>http://xml.org/sax/features/external-parameter-entities</code>
+ * properties.</p>
+ *
+ * @param name the name of the skipped entity. If it is a
+ * parameter entity, the name will begin with '%', and if
+ * it is the external DTD subset, it will be the string
+ * "[dtd]"
+ * @throws org.xml.sax.SAXException any SAX exception, possibly
+ * wrapping another exception
+ */
+ public void skippedEntity (String name)
+ throws SAXException;
+}
+
+// end of ContentHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/DTDHandler.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX DTD handler.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: DTDHandler.java,v 1.2 2004/11/03 22:44:51 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Receive notification of basic DTD-related events.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs information about notations and
+ * unparsed entities, then the application implements this
+ * interface and registers an instance with the SAX parser using
+ * the parser's setDTDHandler method. The parser uses the
+ * instance to report notation and unparsed entity declarations to
+ * the application.</p>
+ *
+ * <p>Note that this interface includes only those DTD events that
+ * the XML recommendation <em>requires</em> processors to report:
+ * notation and unparsed entity declarations.</p>
+ *
+ * <p>The SAX parser may report these events in any order, regardless
+ * of the order in which the notations and unparsed entities were
+ * declared; however, all DTD events must be reported after the
+ * document handler's startDocument event, and before the first
+ * startElement event.
+ * (If the {@link org.xml.sax.ext.LexicalHandler LexicalHandler} is
+ * used, these events must also be reported before the endDTD event.)
+ * </p>
+ *
+ * <p>It is up to the application to store the information for
+ * future use (perhaps in a hash table or object tree).
+ * If the application encounters attributes of type "NOTATION",
+ * "ENTITY", or "ENTITIES", it can use the information that it
+ * obtained through this interface to find the entity and/or
+ * notation corresponding with the attribute value.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setDTDHandler
+ */
+public interface DTDHandler {
+
+
+ /**
+ * Receive notification of a notation declaration event.
+ *
+ * <p>It is up to the application to record the notation for later
+ * reference, if necessary;
+ * notations may appear as attribute values and in unparsed entity
+ * declarations, and are sometime used with processing instruction
+ * target names.</p>
+ *
+ * <p>At least one of publicId and systemId must be non-null.
+ * If a system identifier is present, and it is a URL, the SAX
+ * parser must resolve it fully before passing it to the
+ * application through this event.</p>
+ *
+ * <p>There is no guarantee that the notation declaration will be
+ * reported before any unparsed entities that use it.</p>
+ *
+ * @param name The notation name.
+ * @param publicId The notation's public identifier, or null if
+ * none was given.
+ * @param systemId The notation's system identifier, or null if
+ * none was given.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #unparsedEntityDecl
+ * @see org.xml.sax.Attributes
+ */
+ public abstract void notationDecl (String name,
+ String publicId,
+ String systemId)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of an unparsed entity declaration event.
+ *
+ * <p>Note that the notation name corresponds to a notation
+ * reported by the {@link #notationDecl notationDecl} event.
+ * It is up to the application to record the entity for later
+ * reference, if necessary;
+ * unparsed entities may appear as attribute values.
+ * </p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application.</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @param name The unparsed entity's name.
+ * @param publicId The entity's public identifier, or null if none
+ * was given.
+ * @param systemId The entity's system identifier.
+ * @param notationName The name of the associated notation.
+ * @see #notationDecl
+ * @see org.xml.sax.Attributes
+ */
+ public abstract void unparsedEntityDecl (String name,
+ String publicId,
+ String systemId,
+ String notationName)
+ throws SAXException;
+
+}
+
+// end of DTDHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/EntityResolver.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX entity resolver.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: EntityResolver.java,v 1.2 2004/11/03 22:44:52 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.IOException;
+
+
+/**
+ * Basic interface for resolving entities.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs to implement customized handling
+ * for external entities, it must implement this interface and
+ * register an instance with the SAX driver using the
+ * {@link org.xml.sax.XMLReader#setEntityResolver setEntityResolver}
+ * method.</p>
+ *
+ * <p>The XML reader will then allow the application to intercept any
+ * external entities (including the external DTD subset and external
+ * parameter entities, if any) before including them.</p>
+ *
+ * <p>Many SAX applications will not need to implement this interface,
+ * but it will be especially useful for applications that build
+ * XML documents from databases or other specialised input sources,
+ * or for applications that use URI types other than URLs.</p>
+ *
+ * <p>The following resolver would provide the application
+ * with a special character stream for the entity with the system
+ * identifier "http://www.myhost.com/today":</p>
+ *
+ * <pre>
+ * import org.xml.sax.EntityResolver;
+ * import org.xml.sax.InputSource;
+ *
+ * public class MyResolver implements EntityResolver {
+ * public InputSource resolveEntity (String publicId, String systemId)
+ * {
+ * if (systemId.equals("http://www.myhost.com/today")) {
+ * // return a special input source
+ * MyReader reader = new MyReader();
+ * return new InputSource(reader);
+ * } else {
+ * // use the default behaviour
+ * return null;
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The application can also use this interface to redirect system
+ * identifiers to local URIs or to look up replacements in a catalog
+ * (possibly by using the public identifier).</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setEntityResolver
+ * @see org.xml.sax.InputSource
+ */
+public interface EntityResolver {
+
+
+ /**
+ * Allow the application to resolve external entities.
+ *
+ * <p>The parser will call this method before opening any external
+ * entity except the top-level document entity. Such entities include
+ * the external DTD subset and external parameter entities referenced
+ * within the DTD (in either case, only if the parser reads external
+ * parameter entities), and external general entities referenced
+ * within the document element (if the parser reads external general
+ * entities). The application may request that the parser locate
+ * the entity itself, that it use an alternative URI, or that it
+ * use data provided by the application (as a character or byte
+ * input stream).</p>
+ *
+ * <p>Application writers can use this method to redirect external
+ * system identifiers to secure and/or local URIs, to look up
+ * public identifiers in a catalogue, or to read an entity from a
+ * database or other input source (including, for example, a dialog
+ * box). Neither XML nor SAX specifies a preferred policy for using
+ * public or system IDs to resolve resources. However, SAX specifies
+ * how to interpret any InputSource returned by this method, and that
+ * if none is returned, then the system ID will be dereferenced as
+ * a URL. </p>
+ *
+ * <p>If the system identifier is a URL, the SAX parser must
+ * resolve it fully before reporting it to the application.</p>
+ *
+ * @param publicId The public identifier of the external entity
+ * being referenced, or null if none was supplied.
+ * @param systemId The system identifier of the external entity
+ * being referenced.
+ * @return An InputSource object describing the new input source,
+ * or null to request that the parser open a regular
+ * URI connection to the system identifier.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException A Java-specific IO exception,
+ * possibly the result of creating a new InputStream
+ * or Reader for the InputSource.
+ * @see org.xml.sax.InputSource
+ */
+ public abstract InputSource resolveEntity (String publicId,
+ String systemId)
+ throws SAXException, IOException;
+
+}
+
+// end of EntityResolver.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/ErrorHandler.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX error handler.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: ErrorHandler.java,v 1.2 2004/11/03 22:44:52 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Basic interface for SAX error handlers.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX application needs to implement customized error
+ * handling, it must implement this interface and then register an
+ * instance with the XML reader using the
+ * {@link org.xml.sax.XMLReader#setErrorHandler setErrorHandler}
+ * method. The parser will then report all errors and warnings
+ * through this interface.</p>
+ *
+ * <p><strong>WARNING:</strong> If an application does <em>not</em>
+ * register an ErrorHandler, XML parsing errors will go unreported,
+ * except that <em>SAXParseException</em>s will be thrown for fatal errors.
+ * In order to detect validity errors, an ErrorHandler that does something
+ * with {@link #error error()} calls must be registered.</p>
+ *
+ * <p>For XML processing errors, a SAX driver must use this interface
+ * in preference to throwing an exception: it is up to the application
+ * to decide whether to throw an exception for different types of
+ * errors and warnings. Note, however, that there is no requirement that
+ * the parser continue to report additional errors after a call to
+ * {@link #fatalError fatalError}. In other words, a SAX driver class
+ * may throw an exception after reporting any fatalError.
+ * Also parsers may throw appropriate exceptions for non-XML errors.
+ * For example, {@link XMLReader#parse XMLReader.parse()} would throw
+ * an IOException for errors accessing entities or the document.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#setErrorHandler
+ * @see org.xml.sax.SAXParseException
+ */
+public interface ErrorHandler {
+
+
+ /**
+ * Receive notification of a warning.
+ *
+ * <p>SAX parsers will use this method to report conditions that
+ * are not errors or fatal errors as defined by the XML
+ * recommendation. The default behaviour is to take no
+ * action.</p>
+ *
+ * <p>The SAX parser must continue to provide normal parsing events
+ * after invoking this method: it should still be possible for the
+ * application to process the document through to the end.</p>
+ *
+ * <p>Filters may use this method to report other, non-XML warnings
+ * as well.</p>
+ *
+ * @param exception The warning information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void warning (SAXParseException exception)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a recoverable error.
+ *
+ * <p>This corresponds to the definition of "error" in section 1.2
+ * of the W3C XML 1.0 Recommendation. For example, a validating
+ * parser would use this callback to report the violation of a
+ * validity constraint. The default behaviour is to take no
+ * action.</p>
+ *
+ * <p>The SAX parser must continue to provide normal parsing
+ * events after invoking this method: it should still be possible
+ * for the application to process the document through to the end.
+ * If the application cannot do so, then the parser should report
+ * a fatal error even if the XML recommendation does not require
+ * it to do so.</p>
+ *
+ * <p>Filters may use this method to report other, non-XML errors
+ * as well.</p>
+ *
+ * @param exception The error information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void error (SAXParseException exception)
+ throws SAXException;
+
+
+ /**
+ * Receive notification of a non-recoverable error.
+ *
+ * <p><strong>There is an apparent contradiction between the
+ * documentation for this method and the documentation for {@link
+ * org.xml.sax.ContentHandler#endDocument}. Until this ambiguity
+ * is resolved in a future major release, clients should make no
+ * assumptions about whether endDocument() will or will not be
+ * invoked when the parser has reported a fatalError() or thrown
+ * an exception.</strong></p>
+ *
+ * <p>This corresponds to the definition of "fatal error" in
+ * section 1.2 of the W3C XML 1.0 Recommendation. For example, a
+ * parser would use this callback to report the violation of a
+ * well-formedness constraint.</p>
+ *
+ * <p>The application must assume that the document is unusable
+ * after the parser has invoked this method, and should continue
+ * (if at all) only for the sake of collecting additional error
+ * messages: in fact, SAX parsers are free to stop reporting any
+ * other events once this method has been invoked.</p>
+ *
+ * @param exception The error information encapsulated in a
+ * SAX parse exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.SAXParseException
+ */
+ public abstract void fatalError (SAXParseException exception)
+ throws SAXException;
+
+}
+
+// end of ErrorHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/InputSource.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX input source.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: InputSource.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.Reader;
+import java.io.InputStream;
+
+/**
+ * A single input source for an XML entity.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class allows a SAX application to encapsulate information
+ * about an input source in a single object, which may include
+ * a public identifier, a system identifier, a byte stream (possibly
+ * with a specified encoding), and/or a character stream.</p>
+ *
+ * <p>There are two places that the application can deliver an
+ * input source to the parser: as the argument to the Parser.parse
+ * method, or as the return value of the EntityResolver.resolveEntity
+ * method.</p>
+ *
+ * <p>The SAX parser will use the InputSource object to determine how
+ * to read XML input. If there is a character stream available, the
+ * parser will read that stream directly, disregarding any text
+ * encoding declaration found in that stream.
+ * If there is no character stream, but there is
+ * a byte stream, the parser will use that byte stream, using the
+ * encoding specified in the InputSource or else (if no encoding is
+ * specified) autodetecting the character encoding using an algorithm
+ * such as the one in the XML specification. If neither a character
+ * stream nor a
+ * byte stream is available, the parser will attempt to open a URI
+ * connection to the resource identified by the system
+ * identifier.</p>
+ *
+ * <p>An InputSource object belongs to the application: the SAX parser
+ * shall never modify it in any way (it may modify a copy if
+ * necessary). However, standard processing of both byte and
+ * character streams is to close them on as part of end-of-parse cleanup,
+ * so applications should not attempt to re-use such streams after they
+ * have been handed to a parser. </p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
+ * @see org.xml.sax.EntityResolver#resolveEntity
+ * @see java.io.InputStream
+ * @see java.io.Reader
+ */
+public class InputSource {
+
+ /**
+ * Zero-argument default constructor.
+ *
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setCharacterStream
+ * @see #setEncoding
+ */
+ public InputSource ()
+ {
+ }
+
+
+ /**
+ * Create a new input source with a system identifier.
+ *
+ * <p>Applications may use setPublicId to include a
+ * public identifier as well, or setEncoding to specify
+ * the character encoding, if known.</p>
+ *
+ * <p>If the system identifier is a URL, it must be fully
+ * resolved (it may not be a relative URL).</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setEncoding
+ * @see #setCharacterStream
+ */
+ public InputSource (String systemId)
+ {
+ setSystemId(systemId);
+ }
+
+
+ /**
+ * Create a new input source with a byte stream.
+ *
+ * <p>Application writers should use setSystemId() to provide a base
+ * for resolving relative URIs, may use setPublicId to include a
+ * public identifier, and may use setEncoding to specify the object's
+ * character encoding.</p>
+ *
+ * @param byteStream The raw byte stream containing the document.
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setEncoding
+ * @see #setByteStream
+ * @see #setCharacterStream
+ */
+ public InputSource (InputStream byteStream)
+ {
+ setByteStream(byteStream);
+ }
+
+
+ /**
+ * Create a new input source with a character stream.
+ *
+ * <p>Application writers should use setSystemId() to provide a base
+ * for resolving relative URIs, and may use setPublicId to include a
+ * public identifier.</p>
+ *
+ * <p>The character stream shall not include a byte order mark.</p>
+ *
+ * @see #setPublicId
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #setCharacterStream
+ */
+ public InputSource (Reader characterStream)
+ {
+ setCharacterStream(characterStream);
+ }
+
+
+ /**
+ * Set the public identifier for this input source.
+ *
+ * <p>The public identifier is always optional: if the application
+ * writer includes one, it will be provided as part of the
+ * location information.</p>
+ *
+ * @param publicId The public identifier as a string.
+ * @see #getPublicId
+ * @see org.xml.sax.Locator#getPublicId
+ * @see org.xml.sax.SAXParseException#getPublicId
+ */
+ public void setPublicId (String publicId)
+ {
+ this.publicId = publicId;
+ }
+
+
+ /**
+ * Get the public identifier for this input source.
+ *
+ * @return The public identifier, or null if none was supplied.
+ * @see #setPublicId
+ */
+ public String getPublicId ()
+ {
+ return publicId;
+ }
+
+
+ /**
+ * Set the system identifier for this input source.
+ *
+ * <p>The system identifier is optional if there is a byte stream
+ * or a character stream, but it is still useful to provide one,
+ * since the application can use it to resolve relative URIs
+ * and can include it in error messages and warnings (the parser
+ * will attempt to open a connection to the URI only if
+ * there is no byte stream or character stream specified).</p>
+ *
+ * <p>If the application knows the character encoding of the
+ * object pointed to by the system identifier, it can register
+ * the encoding using the setEncoding method.</p>
+ *
+ * <p>If the system identifier is a URL, it must be fully
+ * resolved (it may not be a relative URL).</p>
+ *
+ * @param systemId The system identifier as a string.
+ * @see #setEncoding
+ * @see #getSystemId
+ * @see org.xml.sax.Locator#getSystemId
+ * @see org.xml.sax.SAXParseException#getSystemId
+ */
+ public void setSystemId (String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+
+ /**
+ * Get the system identifier for this input source.
+ *
+ * <p>The getEncoding method will return the character encoding
+ * of the object pointed to, or null if unknown.</p>
+ *
+ * <p>If the system ID is a URL, it will be fully resolved.</p>
+ *
+ * @return The system identifier, or null if none was supplied.
+ * @see #setSystemId
+ * @see #getEncoding
+ */
+ public String getSystemId ()
+ {
+ return systemId;
+ }
+
+
+ /**
+ * Set the byte stream for this input source.
+ *
+ * <p>The SAX parser will ignore this if there is also a character
+ * stream specified, but it will use a byte stream in preference
+ * to opening a URI connection itself.</p>
+ *
+ * <p>If the application knows the character encoding of the
+ * byte stream, it should set it with the setEncoding method.</p>
+ *
+ * @param byteStream A byte stream containing an XML document or
+ * other entity.
+ * @see #setEncoding
+ * @see #getByteStream
+ * @see #getEncoding
+ * @see java.io.InputStream
+ */
+ public void setByteStream (InputStream byteStream)
+ {
+ this.byteStream = byteStream;
+ }
+
+
+ /**
+ * Get the byte stream for this input source.
+ *
+ * <p>The getEncoding method will return the character
+ * encoding for this byte stream, or null if unknown.</p>
+ *
+ * @return The byte stream, or null if none was supplied.
+ * @see #getEncoding
+ * @see #setByteStream
+ */
+ public InputStream getByteStream ()
+ {
+ return byteStream;
+ }
+
+
+ /**
+ * Set the character encoding, if known.
+ *
+ * <p>The encoding must be a string acceptable for an
+ * XML encoding declaration (see section 4.3.3 of the XML 1.0
+ * recommendation).</p>
+ *
+ * <p>This method has no effect when the application provides a
+ * character stream.</p>
+ *
+ * @param encoding A string describing the character encoding.
+ * @see #setSystemId
+ * @see #setByteStream
+ * @see #getEncoding
+ */
+ public void setEncoding (String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Get the character encoding for a byte stream or URI.
+ * This value will be ignored when the application provides a
+ * character stream.
+ *
+ * @return The encoding, or null if none was supplied.
+ * @see #setByteStream
+ * @see #getSystemId
+ * @see #getByteStream
+ */
+ public String getEncoding ()
+ {
+ return encoding;
+ }
+
+
+ /**
+ * Set the character stream for this input source.
+ *
+ * <p>If there is a character stream specified, the SAX parser
+ * will ignore any byte stream and will not attempt to open
+ * a URI connection to the system identifier.</p>
+ *
+ * @param characterStream The character stream containing the
+ * XML document or other entity.
+ * @see #getCharacterStream
+ * @see java.io.Reader
+ */
+ public void setCharacterStream (Reader characterStream)
+ {
+ this.characterStream = characterStream;
+ }
+
+
+ /**
+ * Get the character stream for this input source.
+ *
+ * @return The character stream, or null if none was supplied.
+ * @see #setCharacterStream
+ */
+ public Reader getCharacterStream ()
+ {
+ return characterStream;
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Internal state.
+ ////////////////////////////////////////////////////////////////////
+
+ private String publicId;
+ private String systemId;
+ private InputStream byteStream;
+ private String encoding;
+ private Reader characterStream;
+
+}
+
+// end of InputSource.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/Locator.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX locator interface for document events.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: Locator.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Interface for associating a SAX event with a document location.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>If a SAX parser provides location information to the SAX
+ * application, it does so by implementing this interface and then
+ * passing an instance to the application using the content
+ * handler's {@link org.xml.sax.ContentHandler#setDocumentLocator
+ * setDocumentLocator} method. The application can use the
+ * object to obtain the location of any other SAX event
+ * in the XML source document.</p>
+ *
+ * <p>Note that the results returned by the object will be valid only
+ * during the scope of each callback method: the application
+ * will receive unpredictable results if it attempts to use the
+ * locator at any other time, or after parsing completes.</p>
+ *
+ * <p>SAX parsers are not required to supply a locator, but they are
+ * very strongly encouraged to do so. If the parser supplies a
+ * locator, it must do so before reporting any other document events.
+ * If no locator has been set by the time the application receives
+ * the {@link org.xml.sax.ContentHandler#startDocument startDocument}
+ * event, the application should assume that a locator is not
+ * available.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @see org.xml.sax.ContentHandler#setDocumentLocator
+ */
+public interface Locator {
+
+
+ /**
+ * Return the public identifier for the current document event.
+ *
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public abstract String getPublicId ();
+
+
+ /**
+ * Return the system identifier for the current document event.
+ *
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application. For example, a file
+ * name must always be provided as a <em>file:...</em> URL, and other
+ * kinds of relative URI are also resolved against their bases.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ public abstract String getSystemId ();
+
+
+ /**
+ * Return the line number where the current document event ends.
+ * Lines are delimited by line ends, which are defined in
+ * the XML specification.
+ *
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * In some cases, these "line" numbers match what would be displayed
+ * as columns, and in others they may not match the source text
+ * due to internal entity expansion. </p>
+ *
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ *
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first line is line 1.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public abstract int getLineNumber ();
+
+
+ /**
+ * Return the column number where the current document event ends.
+ * This is one-based number of Java <code>char</code> values since
+ * the last line end.
+ *
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * For example, when lines contain combining character sequences, wide
+ * characters, surrogate pairs, or bi-directional text, the value may
+ * not correspond to the column in a text editor's display. </p>
+ *
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ *
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first column in each line is column 1.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public abstract int getColumnNumber ();
+
+}
+
+// end of Locator.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXException.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX exception class.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: SAXException.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Encapsulate a general SAX error or warning.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class can contain basic error or warning information from
+ * either the XML parser or the application: a parser writer or
+ * application writer can subclass it to provide additional
+ * functionality. SAX handlers may throw this exception or
+ * any exception subclassed from it.</p>
+ *
+ * <p>If the application needs to pass through other types of
+ * exceptions, it must wrap those exceptions in a SAXException
+ * or an exception derived from a SAXException.</p>
+ *
+ * <p>If the parser or application needs to include information about a
+ * specific location in an XML document, it should use the
+ * {@link org.xml.sax.SAXParseException SAXParseException} subclass.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @version 2.0.1 (sax2r2)
+ * @see org.xml.sax.SAXParseException
+ */
+public class SAXException extends Exception {
+
+
+ /**
+ * Create a new SAXException.
+ */
+ public SAXException ()
+ {
+ super();
+ this.exception = null;
+ }
+
+
+ /**
+ * Create a new SAXException.
+ *
+ * @param message The error or warning message.
+ */
+ public SAXException (String message) {
+ super(message);
+ this.exception = null;
+ }
+
+
+ /**
+ * Create a new SAXException wrapping an existing exception.
+ *
+ * <p>The existing exception will be embedded in the new
+ * one, and its message will become the default message for
+ * the SAXException.</p>
+ *
+ * @param e The exception to be wrapped in a SAXException.
+ */
+ public SAXException (Exception e)
+ {
+ super();
+ this.exception = e;
+ }
+
+
+ /**
+ * Create a new SAXException from an existing exception.
+ *
+ * <p>The existing exception will be embedded in the new
+ * one, but the new exception will have its own message.</p>
+ *
+ * @param message The detail message.
+ * @param e The exception to be wrapped in a SAXException.
+ */
+ public SAXException (String message, Exception e)
+ {
+ super(message);
+ this.exception = e;
+ }
+
+
+ /**
+ * Return a detail message for this exception.
+ *
+ * <p>If there is an embedded exception, and if the SAXException
+ * has no detail message of its own, this method will return
+ * the detail message from the embedded exception.</p>
+ *
+ * @return The error or warning message.
+ */
+ public String getMessage ()
+ {
+ String message = super.getMessage();
+
+ if (message == null && exception != null) {
+ return exception.getMessage();
+ } else {
+ return message;
+ }
+ }
+
+
+ /**
+ * Return the embedded exception, if any.
+ *
+ * @return The embedded exception, or null if there is none.
+ */
+ public Exception getException ()
+ {
+ return exception;
+ }
+
+ /**
+ * Return the cause of the exception
+ *
+ * @return Return the cause of the exception
+ */
+ public Throwable getCause() {
+ return exception;
+ }
+
+ /**
+ * Override toString to pick up any embedded exception.
+ *
+ * @return A string representation of this exception.
+ */
+ public String toString ()
+ {
+ if (exception != null) {
+ return super.toString() + "\n" + exception.toString();
+ } else {
+ return super.toString();
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Internal state.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * @serial The embedded exception if tunnelling, or null.
+ */
+ private Exception exception;
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = 583241635256073760L;
+}
+
+// end of SAXException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXNotRecognizedException.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAXNotRecognizedException.java - unrecognized feature or value.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: SAXNotRecognizedException.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+
+/**
+ * Exception class for an unrecognized identifier.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>An XMLReader will throw this exception when it finds an
+ * unrecognized feature or property identifier; SAX applications and
+ * extensions may use this class for other, similar purposes.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.SAXNotSupportedException
+ */
+public class SAXNotRecognizedException extends SAXException
+{
+
+ /**
+ * Default constructor.
+ */
+ public SAXNotRecognizedException ()
+ {
+ super();
+ }
+
+
+ /**
+ * Construct a new exception with the given message.
+ *
+ * @param message The text message of the exception.
+ */
+ public SAXNotRecognizedException (String message)
+ {
+ super(message);
+ }
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = 5440506620509557213L;
+}
+
+// end of SAXNotRecognizedException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXNotSupportedException.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAXNotSupportedException.java - unsupported feature or value.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: SAXNotSupportedException.java,v 1.4 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Exception class for an unsupported operation.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>An XMLReader will throw this exception when it recognizes a
+ * feature or property identifier, but cannot perform the requested
+ * operation (setting a state or value). Other SAX2 applications and
+ * extensions may use this class for similar purposes.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.SAXNotRecognizedException
+ */
+public class SAXNotSupportedException extends SAXException
+{
+
+ /**
+ * Construct a new exception with no message.
+ */
+ public SAXNotSupportedException ()
+ {
+ super();
+ }
+
+
+ /**
+ * Construct a new exception with the given message.
+ *
+ * @param message The text message of the exception.
+ */
+ public SAXNotSupportedException (String message)
+ {
+ super(message);
+ }
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = -1422818934641823846L;
+}
+
+// end of SAXNotSupportedException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/SAXParseException.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// SAX exception class.
+// http://www.saxproject.org
+// No warranty; no copyright -- use this as you will.
+// $Id: SAXParseException.java,v 1.2 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+/**
+ * Encapsulate an XML parse error or warning.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This exception may include information for locating the error
+ * in the original XML document, as if it came from a {@link Locator}
+ * object. Note that although the application
+ * will receive a SAXParseException as the argument to the handlers
+ * in the {@link org.xml.sax.ErrorHandler ErrorHandler} interface,
+ * the application is not actually required to throw the exception;
+ * instead, it can simply read the information in it and take a
+ * different action.</p>
+ *
+ * <p>Since this exception is a subclass of {@link org.xml.sax.SAXException
+ * SAXException}, it inherits the ability to wrap another exception.</p>
+ *
+ * @since SAX 1.0
+ * @author David Megginson
+ * @version 2.0.1 (sax2r2)
+ * @see org.xml.sax.SAXException
+ * @see org.xml.sax.Locator
+ * @see org.xml.sax.ErrorHandler
+ */
+public class SAXParseException extends SAXException {
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Constructors.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Create a new SAXParseException from a message and a Locator.
+ *
+ * <p>This constructor is especially useful when an application is
+ * creating its own exception from within a {@link org.xml.sax.ContentHandler
+ * ContentHandler} callback.</p>
+ *
+ * @param message The error or warning message.
+ * @param locator The locator object for the error or warning (may be
+ * null).
+ * @see org.xml.sax.Locator
+ */
+ public SAXParseException (String message, Locator locator) {
+ super(message);
+ if (locator != null) {
+ init(locator.getPublicId(), locator.getSystemId(),
+ locator.getLineNumber(), locator.getColumnNumber());
+ } else {
+ init(null, null, -1, -1);
+ }
+ }
+
+
+ /**
+ * Wrap an existing exception in a SAXParseException.
+ *
+ * <p>This constructor is especially useful when an application is
+ * creating its own exception from within a {@link org.xml.sax.ContentHandler
+ * ContentHandler} callback, and needs to wrap an existing exception that is not a
+ * subclass of {@link org.xml.sax.SAXException SAXException}.</p>
+ *
+ * @param message The error or warning message, or null to
+ * use the message from the embedded exception.
+ * @param locator The locator object for the error or warning (may be
+ * null).
+ * @param e Any exception.
+ * @see org.xml.sax.Locator
+ */
+ public SAXParseException (String message, Locator locator,
+ Exception e) {
+ super(message, e);
+ if (locator != null) {
+ init(locator.getPublicId(), locator.getSystemId(),
+ locator.getLineNumber(), locator.getColumnNumber());
+ } else {
+ init(null, null, -1, -1);
+ }
+ }
+
+
+ /**
+ * Create a new SAXParseException.
+ *
+ * <p>This constructor is most useful for parser writers.</p>
+ *
+ * <p>All parameters except the message are as if
+ * they were provided by a {@link Locator}. For example, if the
+ * system identifier is a URL (including relative filename), the
+ * caller must resolve it fully before creating the exception.</p>
+ *
+ *
+ * @param message The error or warning message.
+ * @param publicId The public identifier of the entity that generated
+ * the error or warning.
+ * @param systemId The system identifier of the entity that generated
+ * the error or warning.
+ * @param lineNumber The line number of the end of the text that
+ * caused the error or warning.
+ * @param columnNumber The column number of the end of the text that
+ * cause the error or warning.
+ */
+ public SAXParseException (String message, String publicId, String systemId,
+ int lineNumber, int columnNumber)
+ {
+ super(message);
+ init(publicId, systemId, lineNumber, columnNumber);
+ }
+
+
+ /**
+ * Create a new SAXParseException with an embedded exception.
+ *
+ * <p>This constructor is most useful for parser writers who
+ * need to wrap an exception that is not a subclass of
+ * {@link org.xml.sax.SAXException SAXException}.</p>
+ *
+ * <p>All parameters except the message and exception are as if
+ * they were provided by a {@link Locator}. For example, if the
+ * system identifier is a URL (including relative filename), the
+ * caller must resolve it fully before creating the exception.</p>
+ *
+ * @param message The error or warning message, or null to use
+ * the message from the embedded exception.
+ * @param publicId The public identifier of the entity that generated
+ * the error or warning.
+ * @param systemId The system identifier of the entity that generated
+ * the error or warning.
+ * @param lineNumber The line number of the end of the text that
+ * caused the error or warning.
+ * @param columnNumber The column number of the end of the text that
+ * cause the error or warning.
+ * @param e Another exception to embed in this one.
+ */
+ public SAXParseException (String message, String publicId, String systemId,
+ int lineNumber, int columnNumber, Exception e)
+ {
+ super(message, e);
+ init(publicId, systemId, lineNumber, columnNumber);
+ }
+
+
+ /**
+ * Internal initialization method.
+ *
+ * @param publicId The public identifier of the entity which generated the exception,
+ * or null.
+ * @param systemId The system identifier of the entity which generated the exception,
+ * or null.
+ * @param lineNumber The line number of the error, or -1.
+ * @param columnNumber The column number of the error, or -1.
+ */
+ private void init (String publicId, String systemId,
+ int lineNumber, int columnNumber)
+ {
+ this.publicId = publicId;
+ this.systemId = systemId;
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+
+ /**
+ * Get the public identifier of the entity where the exception occurred.
+ *
+ * @return A string containing the public identifier, or null
+ * if none is available.
+ * @see org.xml.sax.Locator#getPublicId
+ */
+ public String getPublicId ()
+ {
+ return this.publicId;
+ }
+
+
+ /**
+ * Get the system identifier of the entity where the exception occurred.
+ *
+ * <p>If the system identifier is a URL, it will have been resolved
+ * fully.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see org.xml.sax.Locator#getSystemId
+ */
+ public String getSystemId ()
+ {
+ return this.systemId;
+ }
+
+
+ /**
+ * The line number of the end of the text where the exception occurred.
+ *
+ * <p>The first line is line 1.</p>
+ *
+ * @return An integer representing the line number, or -1
+ * if none is available.
+ * @see org.xml.sax.Locator#getLineNumber
+ */
+ public int getLineNumber ()
+ {
+ return this.lineNumber;
+ }
+
+
+ /**
+ * The column number of the end of the text where the exception occurred.
+ *
+ * <p>The first column in a line is position 1.</p>
+ *
+ * @return An integer representing the column number, or -1
+ * if none is available.
+ * @see org.xml.sax.Locator#getColumnNumber
+ */
+ public int getColumnNumber ()
+ {
+ return this.columnNumber;
+ }
+
+ /**
+ * Override toString to provide more detailed error message.
+ *
+ * @return A string representation of this exception.
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder(getClass().getName());
+ String message = getLocalizedMessage();
+ if (publicId!=null) buf.append("publicId: ").append(publicId);
+ if (systemId!=null) buf.append("; systemId: ").append(systemId);
+ if (lineNumber!=-1) buf.append("; lineNumber: ").append(lineNumber);
+ if (columnNumber!=-1) buf.append("; columnNumber: ").append(columnNumber);
+
+ //append the exception message at the end
+ if (message!=null) buf.append("; ").append(message);
+ return buf.toString();
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // Internal state.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * @serial The public identifier, or null.
+ * @see #getPublicId
+ */
+ private String publicId;
+
+
+ /**
+ * @serial The system identifier, or null.
+ * @see #getSystemId
+ */
+ private String systemId;
+
+
+ /**
+ * @serial The line number, or -1.
+ * @see #getLineNumber
+ */
+ private int lineNumber;
+
+
+ /**
+ * @serial The column number, or -1.
+ * @see #getColumnNumber
+ */
+ private int columnNumber;
+
+ // Added serialVersionUID to preserve binary compatibility
+ static final long serialVersionUID = -5651165872476709336L;
+}
+
+// end of SAXParseException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/XMLReader.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2000, 2005, 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.
+ */
+
+// XMLReader.java - read an XML document.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the Public Domain.
+// $Id: XMLReader.java,v 1.3 2004/11/03 22:55:32 jsuttor Exp $
+
+package jdk.internal.org.xml.sax;
+
+import java.io.IOException;
+
+
+/**
+ * Interface for reading an XML document using callbacks.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p><strong>Note:</strong> despite its name, this interface does
+ * <em>not</em> extend the standard Java {@link java.io.Reader Reader}
+ * interface, because reading XML is a fundamentally different activity
+ * than reading character data.</p>
+ *
+ * <p>XMLReader is the interface that an XML parser's SAX2 driver must
+ * implement. This interface allows an application to set and
+ * query features and properties in the parser, to register
+ * event handlers for document processing, and to initiate
+ * a document parse.</p>
+ *
+ * <p>All SAX interfaces are assumed to be synchronous: the
+ * {@link #parse parse} methods must not return until parsing
+ * is complete, and readers must wait for an event-handler callback
+ * to return before reporting the next event.</p>
+ *
+ * <p>This interface replaces the (now deprecated) SAX 1.0 {@link
+ * org.xml.sax.Parser Parser} interface. The XMLReader interface
+ * contains two important enhancements over the old Parser
+ * interface (as well as some minor ones):</p>
+ *
+ * <ol>
+ * <li>it adds a standard way to query and set features and
+ * properties; and</li>
+ * <li>it adds Namespace support, which is required for many
+ * higher-level XML standards.</li>
+ * </ol>
+ *
+ * <p>There are adapters available to convert a SAX1 Parser to
+ * a SAX2 XMLReader and vice-versa.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson
+ * @see org.xml.sax.XMLFilter
+ * @see org.xml.sax.helpers.ParserAdapter
+ * @see org.xml.sax.helpers.XMLReaderAdapter
+ */
+public interface XMLReader
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Configuration.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Look up the value of a feature flag.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a feature name but
+ * temporarily be unable to return its value.
+ * Some feature values may be available only in specific
+ * contexts, such as before, during, or after a parse.
+ * Also, some feature values may not be programmatically accessible.
+ * (In the case of an adapter for SAX1 {@link Parser}, there is no
+ * implementation-independent way to expose whether the underlying
+ * parser is performing validation, expanding external entities,
+ * and so forth.) </p>
+ *
+ * <p>All XMLReaders are required to recognize the
+ * http://xml.org/sax/features/namespaces and the
+ * http://xml.org/sax/features/namespace-prefixes feature names.</p>
+ *
+ * <p>Typical usage is something like this:</p>
+ *
+ * <pre>
+ * XMLReader r = new MySAXDriver();
+ *
+ * // try to activate validation
+ * try {
+ * r.setFeature("http://xml.org/sax/features/validation", true);
+ * } catch (SAXException e) {
+ * System.err.println("Cannot activate validation.");
+ * }
+ *
+ * // register event handlers
+ * r.setContentHandler(new MyContentHandler());
+ * r.setErrorHandler(new MyErrorHandler());
+ *
+ * // parse the first document
+ * try {
+ * r.parse("http://www.foo.com/mydoc.xml");
+ * } catch (IOException e) {
+ * System.err.println("I/O exception reading XML document");
+ * } catch (SAXException e) {
+ * System.err.println("XML exception reading document.");
+ * }
+ * </pre>
+ *
+ * <p>Implementors are free (and encouraged) to invent their own features,
+ * using names built on their own URIs.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @return The current value of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException If the feature
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot determine its value at this time.
+ * @see #setFeature
+ */
+ public boolean getFeature (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Set the value of a feature flag.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to expose a feature value but
+ * to be unable to change the current value.
+ * Some feature values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * <p>All XMLReaders are required to support setting
+ * http://xml.org/sax/features/namespaces to true and
+ * http://xml.org/sax/features/namespace-prefixes to false.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @param value The requested value of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException If the feature
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot set the requested value.
+ * @see #getFeature
+ */
+ public void setFeature (String name, boolean value)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Look up the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * temporarily be unable to return its value.
+ * Some property values may be available only in specific
+ * contexts, such as before, during, or after a parse.</p>
+ *
+ * <p>XMLReaders are not required to recognize any specific
+ * property names, though an initial core set is documented for
+ * SAX2.</p>
+ *
+ * <p>Implementors are free (and encouraged) to invent their own properties,
+ * using names built on their own URIs.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @return The current value of the property.
+ * @exception org.xml.sax.SAXNotRecognizedException If the property
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot determine its value at this time.
+ * @see #setProperty
+ */
+ public Object getProperty (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+ /**
+ * Set the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * to be unable to change the current value.
+ * Some property values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * <p>XMLReaders are not required to recognize setting
+ * any specific property names, though a core set is defined by
+ * SAX2.</p>
+ *
+ * <p>This method is also the standard mechanism for setting
+ * extended handlers.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @param value The requested value for the property.
+ * @exception org.xml.sax.SAXNotRecognizedException If the property
+ * value can't be assigned or retrieved.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot set the requested value.
+ */
+ public void setProperty (String name, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException;
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Event handlers.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Allow an application to register an entity resolver.
+ *
+ * <p>If the application does not register an entity resolver,
+ * the XMLReader will perform its own default resolution.</p>
+ *
+ * <p>Applications may register a new or different resolver in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * resolver immediately.</p>
+ *
+ * @param resolver The entity resolver.
+ * @see #getEntityResolver
+ */
+ public void setEntityResolver (EntityResolver resolver);
+
+
+ /**
+ * Return the current entity resolver.
+ *
+ * @return The current entity resolver, or null if none
+ * has been registered.
+ * @see #setEntityResolver
+ */
+ public EntityResolver getEntityResolver ();
+
+
+ /**
+ * Allow an application to register a DTD event handler.
+ *
+ * <p>If the application does not register a DTD handler, all DTD
+ * events reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The DTD handler.
+ * @see #getDTDHandler
+ */
+ public void setDTDHandler (DTDHandler handler);
+
+
+ /**
+ * Return the current DTD handler.
+ *
+ * @return The current DTD handler, or null if none
+ * has been registered.
+ * @see #setDTDHandler
+ */
+ public DTDHandler getDTDHandler ();
+
+
+ /**
+ * Allow an application to register a content event handler.
+ *
+ * <p>If the application does not register a content handler, all
+ * content events reported by the SAX parser will be silently
+ * ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The content handler.
+ * @see #getContentHandler
+ */
+ public void setContentHandler (ContentHandler handler);
+
+
+ /**
+ * Return the current content handler.
+ *
+ * @return The current content handler, or null if none
+ * has been registered.
+ * @see #setContentHandler
+ */
+ public ContentHandler getContentHandler ();
+
+
+ /**
+ * Allow an application to register an error event handler.
+ *
+ * <p>If the application does not register an error handler, all
+ * error events reported by the SAX parser will be silently
+ * ignored; however, normal processing may not continue. It is
+ * highly recommended that all SAX applications implement an
+ * error handler to avoid unexpected bugs.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The error handler.
+ * @see #getErrorHandler
+ */
+ public void setErrorHandler (ErrorHandler handler);
+
+
+ /**
+ * Return the current error handler.
+ *
+ * @return The current error handler, or null if none
+ * has been registered.
+ * @see #setErrorHandler
+ */
+ public ErrorHandler getErrorHandler ();
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Parsing.
+ ////////////////////////////////////////////////////////////////////
+
+ /**
+ * Parse an XML document.
+ *
+ * <p>The application can use this method to instruct the XML
+ * reader to begin parsing an XML document from any valid input
+ * source (a character stream, a byte stream, or a URI).</p>
+ *
+ * <p>Applications may not invoke this method while a parse is in
+ * progress (they should create a new XMLReader instead for each
+ * nested XML document). Once a parse is complete, an
+ * application may reuse the same XMLReader object, possibly with a
+ * different input source.
+ * Configuration of the XMLReader object (such as handler bindings and
+ * values established for feature flags and properties) is unchanged
+ * by completion of a parse, unless the definition of that aspect of
+ * the configuration explicitly specifies other behavior.
+ * (For example, feature flags or properties exposing
+ * characteristics of the document being parsed.)
+ * </p>
+ *
+ * <p>During the parse, the XMLReader will provide information
+ * about the XML document through the registered event
+ * handlers.</p>
+ *
+ * <p>This method is synchronous: it will not return until parsing
+ * has ended. If a client application wants to terminate
+ * parsing early, it should throw an exception.</p>
+ *
+ * @param input The input source for the top-level of the
+ * XML document.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see org.xml.sax.InputSource
+ * @see #parse(java.lang.String)
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ */
+ public void parse (InputSource input)
+ throws IOException, SAXException;
+
+
+ /**
+ * Parse an XML document from a system identifier (URI).
+ *
+ * <p>This method is a shortcut for the common case of reading a
+ * document from a system identifier. It is the exact
+ * equivalent of the following:</p>
+ *
+ * <pre>
+ * parse(new InputSource(systemId));
+ * </pre>
+ *
+ * <p>If the system identifier is a URL, it must be fully resolved
+ * by the application before it is passed to the parser.</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see #parse(org.xml.sax.InputSource)
+ */
+ public void parse (String systemId)
+ throws IOException, SAXException;
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/org/xml/sax/helpers/DefaultHandler.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2000, 2006, 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.
+ */
+
+// DefaultHandler.java - default implementation of the core handlers.
+// http://www.saxproject.org
+// Written by David Megginson
+// NO WARRANTY! This class is in the public domain.
+// $Id: DefaultHandler.java,v 1.3 2006/04/13 02:06:32 jeffsuttor Exp $
+
+package jdk.internal.org.xml.sax.helpers;
+
+import java.io.IOException;
+
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.Locator;
+import jdk.internal.org.xml.sax.Attributes;
+import jdk.internal.org.xml.sax.EntityResolver;
+import jdk.internal.org.xml.sax.DTDHandler;
+import jdk.internal.org.xml.sax.ContentHandler;
+import jdk.internal.org.xml.sax.ErrorHandler;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+
+
+/**
+ * Default base class for SAX2 event handlers.
+ *
+ * <blockquote>
+ * <em>This module, both source code and documentation, is in the
+ * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
+ * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
+ * for further information.
+ * </blockquote>
+ *
+ * <p>This class is available as a convenience base class for SAX2
+ * applications: it provides default implementations for all of the
+ * callbacks in the four core SAX2 handler classes:</p>
+ *
+ * <ul>
+ * <li>{@link org.xml.sax.EntityResolver EntityResolver}</li>
+ * <li>{@link org.xml.sax.DTDHandler DTDHandler}</li>
+ * <li>{@link org.xml.sax.ContentHandler ContentHandler}</li>
+ * <li>{@link org.xml.sax.ErrorHandler ErrorHandler}</li>
+ * </ul>
+ *
+ * <p>Application writers can extend this class when they need to
+ * implement only part of an interface; parser writers can
+ * instantiate this class to provide default handlers when the
+ * application has not supplied its own.</p>
+ *
+ * <p>This class replaces the deprecated SAX1
+ * {@link org.xml.sax.HandlerBase HandlerBase} class.</p>
+ *
+ * @since SAX 2.0
+ * @author David Megginson,
+ * @see org.xml.sax.EntityResolver
+ * @see org.xml.sax.DTDHandler
+ * @see org.xml.sax.ContentHandler
+ * @see org.xml.sax.ErrorHandler
+ */
+public class DefaultHandler
+ implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+{
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of the EntityResolver interface.
+ ////////////////////////////////////////////////////////////////////
+
+ /**
+ * Resolve an external entity.
+ *
+ * <p>Always return null, so that the parser will use the system
+ * identifier provided in the XML document. This method implements
+ * the SAX default behaviour: application writers can override it
+ * in a subclass to do special translations such as catalog lookups
+ * or URI redirection.</p>
+ *
+ * @param publicId The public identifier, or null if none is
+ * available.
+ * @param systemId The system identifier provided in the XML
+ * document.
+ * @return The new input source, or null to require the
+ * default behaviour.
+ * @exception java.io.IOException If there is an error setting
+ * up the new input source.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.EntityResolver#resolveEntity
+ */
+ public InputSource resolveEntity (String publicId, String systemId)
+ throws IOException, SAXException
+ {
+ return null;
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of DTDHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive notification of a notation declaration.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass if they wish to keep track of the notations
+ * declared in a document.</p>
+ *
+ * @param name The notation name.
+ * @param publicId The notation public identifier, or null if not
+ * available.
+ * @param systemId The notation system identifier.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.DTDHandler#notationDecl
+ */
+ public void notationDecl (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of an unparsed entity declaration.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to keep track of the unparsed entities
+ * declared in a document.</p>
+ *
+ * @param name The entity name.
+ * @param publicId The entity public identifier, or null if not
+ * available.
+ * @param systemId The entity system identifier.
+ * @param notationName The name of the associated notation.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.DTDHandler#unparsedEntityDecl
+ */
+ public void unparsedEntityDecl (String name, String publicId,
+ String systemId, String notationName)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of ContentHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive a Locator object for document events.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass if they wish to store the locator for use
+ * with other document events.</p>
+ *
+ * @param locator A locator for all SAX document events.
+ * @see org.xml.sax.ContentHandler#setDocumentLocator
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator (Locator locator)
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the beginning of the document.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the beginning
+ * of a document (such as allocating the root node of a tree or
+ * creating an output file).</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startDocument
+ */
+ public void startDocument ()
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of the document.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end
+ * of a document (such as finalising a tree or closing an output
+ * file).</p>
+ *
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endDocument
+ */
+ public void endDocument ()
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the start of a Namespace mapping.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the start of
+ * each Namespace prefix scope (such as storing the prefix mapping).</p>
+ *
+ * @param prefix The Namespace prefix being declared.
+ * @param uri The Namespace URI mapped to the prefix.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startPrefixMapping
+ */
+ public void startPrefixMapping (String prefix, String uri)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of a Namespace mapping.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end of
+ * each prefix mapping.</p>
+ *
+ * @param prefix The Namespace prefix being declared.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endPrefixMapping
+ */
+ public void endPrefixMapping (String prefix)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the start of an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the start of
+ * each element (such as allocating a new tree node or writing
+ * output to a file).</p>
+ *
+ * @param uri The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param localName The local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed.
+ * @param qName The qualified name (with prefix), or the
+ * empty string if qualified names are not available.
+ * @param attributes The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#startElement
+ */
+ public void startElement (String uri, String localName,
+ String qName, Attributes attributes)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of the end of an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions at the end of
+ * each element (such as finalising a tree node or writing
+ * output to a file).</p>
+ *
+ * @param uri The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param localName The local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed.
+ * @param qName The qualified name (with prefix), or the
+ * empty string if qualified names are not available.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#endElement
+ */
+ public void endElement (String uri, String localName, String qName)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of character data inside an element.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method to take specific actions for each chunk of character data
+ * (such as adding the data to a node or buffer, or printing it to
+ * a file).</p>
+ *
+ * @param ch The characters.
+ * @param start The start position in the character array.
+ * @param length The number of characters to use from the
+ * character array.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#characters
+ */
+ public void characters (char ch[], int start, int length)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of ignorable whitespace in element content.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method to take specific actions for each chunk of ignorable
+ * whitespace (such as adding data to a node or buffer, or printing
+ * it to a file).</p>
+ *
+ * @param ch The whitespace characters.
+ * @param start The start position in the character array.
+ * @param length The number of characters to use from the
+ * character array.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#ignorableWhitespace
+ */
+ public void ignorableWhitespace (char ch[], int start, int length)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a processing instruction.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions for each
+ * processing instruction, such as setting status variables or
+ * invoking other methods.</p>
+ *
+ * @param target The processing instruction target.
+ * @param data The processing instruction data, or null if
+ * none is supplied.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#processingInstruction
+ */
+ public void processingInstruction (String target, String data)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a skipped entity.
+ *
+ * <p>By default, do nothing. Application writers may override this
+ * method in a subclass to take specific actions for each
+ * processing instruction, such as setting status variables or
+ * invoking other methods.</p>
+ *
+ * @param name The name of the skipped entity.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ContentHandler#processingInstruction
+ */
+ public void skippedEntity (String name)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Default implementation of the ErrorHandler interface.
+ ////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Receive notification of a parser warning.
+ *
+ * <p>The default implementation does nothing. Application writers
+ * may override this method in a subclass to take specific actions
+ * for each warning, such as inserting the message in a log file or
+ * printing it to the console.</p>
+ *
+ * @param e The warning information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#warning
+ * @see org.xml.sax.SAXParseException
+ */
+ public void warning (SAXParseException e)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Receive notification of a recoverable parser error.
+ *
+ * <p>The default implementation does nothing. Application writers
+ * may override this method in a subclass to take specific actions
+ * for each error, such as inserting the message in a log file or
+ * printing it to the console.</p>
+ *
+ * @param e The error information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#warning
+ * @see org.xml.sax.SAXParseException
+ */
+ public void error (SAXParseException e)
+ throws SAXException
+ {
+ // no op
+ }
+
+
+ /**
+ * Report a fatal XML parsing error.
+ *
+ * <p>The default implementation throws a SAXParseException.
+ * Application writers may override this method in a subclass if
+ * they need to take specific actions for each fatal error (such as
+ * collecting all of the errors into a single report): in any case,
+ * the application must stop all regular processing when this
+ * method is invoked, since the document is no longer reliable, and
+ * the parser may no longer report parsing events.</p>
+ *
+ * @param e The error information encoded as an exception.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see org.xml.sax.ErrorHandler#fatalError
+ * @see org.xml.sax.SAXParseException
+ */
+ public void fatalError (SAXParseException e)
+ throws SAXException
+ {
+ throw e;
+ }
+
+}
+
+// end of DefaultHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/BasicXmlPropertiesProvider.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+package jdk.internal.util.xml;
+
+import java.util.Properties;
+import java.util.InvalidPropertiesFormatException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+import sun.util.spi.XmlPropertiesProvider;
+
+/**
+ * A {@code XmlPropertiesProvider} implementation that uses the UKit XML parser.
+ */
+
+public class BasicXmlPropertiesProvider extends XmlPropertiesProvider {
+
+ public BasicXmlPropertiesProvider() { }
+
+ @Override
+ public void load(Properties props, InputStream in)
+ throws IOException, InvalidPropertiesFormatException
+ {
+ PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
+ handler.load(props, in);
+ }
+
+ @Override
+ public void store(Properties props, OutputStream os, String comment,
+ String encoding)
+ throws IOException
+ {
+ PropertiesDefaultHandler handler = new PropertiesDefaultHandler();
+ handler.store(props, os, comment, encoding);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/PropertiesDefaultHandler.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml;
+
+import java.io.*;
+import java.util.InvalidPropertiesFormatException;
+import java.util.Properties;
+import jdk.internal.org.xml.sax.Attributes;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+import jdk.internal.util.xml.impl.SAXParserImpl;
+import jdk.internal.util.xml.impl.XMLStreamWriterImpl;
+
+/**
+ * A class used to aid in Properties load and save in XML. This class is
+ * re-implemented using a subset of SAX
+ *
+ * @author Joe Wang
+ * @since 8
+ */
+public class PropertiesDefaultHandler extends DefaultHandler {
+
+ // Elements specified in the properties.dtd
+ private static final String ELEMENT_ROOT = "properties";
+ private static final String ELEMENT_COMMENT = "comment";
+ private static final String ELEMENT_ENTRY = "entry";
+ private static final String ATTR_KEY = "key";
+ // The required DTD URI for exported properties
+ private static final String PROPS_DTD_DECL =
+ "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">";
+ private static final String PROPS_DTD_URI =
+ "http://java.sun.com/dtd/properties.dtd";
+ private static final String PROPS_DTD =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<!-- DTD for properties -->"
+ + "<!ELEMENT properties ( comment?, entry* ) >"
+ + "<!ATTLIST properties"
+ + " version CDATA #FIXED \"1.0\">"
+ + "<!ELEMENT comment (#PCDATA) >"
+ + "<!ELEMENT entry (#PCDATA) >"
+ + "<!ATTLIST entry "
+ + " key CDATA #REQUIRED>";
+ /**
+ * Version number for the format of exported properties files.
+ */
+ private static final String EXTERNAL_XML_VERSION = "1.0";
+ private Properties properties;
+
+ public void load(Properties props, InputStream in)
+ throws IOException, InvalidPropertiesFormatException, UnsupportedEncodingException
+ {
+ this.properties = props;
+
+ try {
+ SAXParser parser = new SAXParserImpl();
+ parser.parse(in, this);
+ } catch (SAXException saxe) {
+ throw new InvalidPropertiesFormatException(saxe);
+ }
+
+ /**
+ * String xmlVersion = propertiesElement.getAttribute("version"); if
+ * (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) throw new
+ * InvalidPropertiesFormatException( "Exported Properties file format
+ * version " + xmlVersion + " is not supported. This java installation
+ * can read" + " versions " + EXTERNAL_XML_VERSION + " or older. You" +
+ * " may need to install a newer version of JDK.");
+ */
+ }
+
+ public void store(Properties props, OutputStream os, String comment, String encoding)
+ throws IOException
+ {
+ try {
+ XMLStreamWriter writer = new XMLStreamWriterImpl(os, encoding);
+ writer.writeStartDocument();
+ writer.writeDTD(PROPS_DTD_DECL);
+ writer.writeStartElement(ELEMENT_ROOT);
+ if (comment != null && comment.length() > 0) {
+ writer.writeStartElement(ELEMENT_COMMENT);
+ writer.writeCharacters(comment);
+ writer.writeEndElement();
+ }
+
+ for (String key : props.stringPropertyNames()) {
+ String val = props.getProperty(key);
+ writer.writeStartElement(ELEMENT_ENTRY);
+ writer.writeAttribute(ATTR_KEY, key);
+ writer.writeCharacters(val);
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.close();
+ } catch (XMLStreamException e) {
+ if (e.getCause() instanceof UnsupportedEncodingException) {
+ throw (UnsupportedEncodingException) e.getCause();
+ }
+ throw new IOException(e);
+ }
+
+ }
+ ////////////////////////////////////////////////////////////////////
+ // Validate while parsing
+ ////////////////////////////////////////////////////////////////////
+ final static String ALLOWED_ELEMENTS = "properties, comment, entry";
+ final static String ALLOWED_COMMENT = "comment";
+ ////////////////////////////////////////////////////////////////////
+ // Handler methods
+ ////////////////////////////////////////////////////////////////////
+ StringBuffer buf = new StringBuffer();
+ boolean sawComment = false;
+ boolean validEntry = false;
+ int rootElem = 0;
+ String key;
+ String rootElm;
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException
+ {
+ if (rootElem < 2) {
+ rootElem++;
+ }
+
+ if (rootElm == null) {
+ fatalError(new SAXParseException("An XML properties document must contain"
+ + " the DOCTYPE declaration as defined by java.util.Properties.", null));
+ }
+
+ if (rootElem == 1 && !rootElm.equals(qName)) {
+ fatalError(new SAXParseException("Document root element \"" + qName
+ + "\", must match DOCTYPE root \"" + rootElm + "\"", null));
+ }
+ if (!ALLOWED_ELEMENTS.contains(qName)) {
+ fatalError(new SAXParseException("Element type \"" + qName + "\" must be declared.", null));
+ }
+ if (qName.equals(ELEMENT_ENTRY)) {
+ validEntry = true;
+ key = attributes.getValue(ATTR_KEY);
+ if (key == null) {
+ fatalError(new SAXParseException("Attribute \"key\" is required and must be specified for element type \"entry\"", null));
+ }
+ } else if (qName.equals(ALLOWED_COMMENT)) {
+ if (sawComment) {
+ fatalError(new SAXParseException("Only one comment element may be allowed. "
+ + "The content of element type \"properties\" must match \"(comment?,entry*)\"", null));
+ }
+ sawComment = true;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (validEntry) {
+ buf.append(ch, start, length);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!ALLOWED_ELEMENTS.contains(qName)) {
+ fatalError(new SAXParseException("Element: " + qName + " is invalid, must match \"(comment?,entry*)\".", null));
+ }
+
+ if (validEntry) {
+ properties.setProperty(key, buf.toString());
+ buf.delete(0, buf.length());
+ validEntry = false;
+ }
+ }
+
+ @Override
+ public void notationDecl(String name, String publicId, String systemId) throws SAXException {
+ rootElm = name;
+ }
+
+ @Override
+ public InputSource resolveEntity(String pubid, String sysid)
+ throws SAXException, IOException {
+ {
+ if (sysid.equals(PROPS_DTD_URI)) {
+ InputSource is;
+ is = new InputSource(new StringReader(PROPS_DTD));
+ is.setSystemId(PROPS_DTD_URI);
+ return is;
+ }
+ throw new SAXException("Invalid system identifier: " + sysid);
+ }
+ }
+
+ @Override
+ public void error(SAXParseException x) throws SAXException {
+ throw x;
+ }
+
+ @Override
+ public void fatalError(SAXParseException x) throws SAXException {
+ throw x;
+ }
+
+ @Override
+ public void warning(SAXParseException x) throws SAXException {
+ throw x;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/SAXParser.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * Defines the API that wraps an {@link org.xml.sax.XMLReader}
+ * implementation class. In JAXP 1.0, this class wrapped the
+ * {@link org.xml.sax.Parser} interface, however this interface was
+ * replaced by the {@link org.xml.sax.XMLReader}. For ease
+ * of transition, this class continues to support the same name
+ * and interface as well as supporting new methods.
+ *
+ * An instance of this class can be obtained from the
+ * {@link javax.xml.parsers.SAXParserFactory#newSAXParser()} method.
+ * Once an instance of this class is obtained, XML can be parsed from
+ * a variety of input sources. These input sources are InputStreams,
+ * Files, URLs, and SAX InputSources.<p>
+ *
+ * This static method creates a new factory instance based
+ * on a system property setting or uses the platform default
+ * if no property has been defined.<p>
+ *
+ * The system property that controls which Factory implementation
+ * to create is named <code>"javax.xml.parsers.SAXParserFactory"</code>.
+ * This property names a class that is a concrete subclass of this
+ * abstract class. If no property is defined, a platform default
+ * will be used.</p>
+ *
+ * As the content is parsed by the underlying parser, methods of the
+ * given
+ * {@link org.xml.sax.helpers.DefaultHandler} are called.<p>
+ *
+ * Implementors of this class which wrap an underlaying implementation
+ * can consider using the {@link org.xml.sax.helpers.ParserAdapter}
+ * class to initially adapt their SAX1 implementation to work under
+ * this revised class.
+ *
+ * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
+ * @version $Revision: 1.8 $, $Date: 2010-11-01 04:36:09 $
+ *
+ * @author Joe Wang
+ * This is a subset of that in JAXP, javax.xml.parsers.SAXParser
+ *
+ */
+public abstract class SAXParser {
+
+ /**
+ * <p>Protected constructor to prevent instantiation.</p>
+ */
+ protected SAXParser() {
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream}
+ * instance as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is InputStream containing the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the given InputStream is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(InputStream is, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (is == null) {
+ throw new IllegalArgumentException("InputStream cannot be null");
+ }
+
+ InputSource input = new InputSource(is);
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content described by the giving Uniform Resource
+ * Identifier (URI) as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param uri The location of the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the uri is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(String uri, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri cannot be null");
+ }
+
+ InputSource input = new InputSource(uri);
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content of the file specified as XML using the
+ * specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param f The file containing the XML to parse
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the File object is null.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(File f, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (f == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ //convert file to appropriate URI, f.toURI().toASCIIString()
+ //converts the URI to string as per rule specified in
+ //RFC 2396,
+ InputSource input = new InputSource(f.toURI().toASCIIString());
+ this.parse(input, dh);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource}
+ * as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param dh The SAX DefaultHandler to use.
+ *
+ * @throws IllegalArgumentException If the <code>InputSource</code> object
+ * is <code>null</code>.
+ * @throws IOException If any IO errors occur.
+ * @throws SAXException If any SAX errors occur during processing.
+ *
+ * @see org.xml.sax.DocumentHandler
+ */
+ public void parse(InputSource is, DefaultHandler dh)
+ throws SAXException, IOException
+ {
+ if (is == null) {
+ throw new IllegalArgumentException("InputSource cannot be null");
+ }
+
+ XMLReader reader = this.getXMLReader();
+ if (dh != null) {
+ reader.setContentHandler(dh);
+ reader.setEntityResolver(dh);
+ reader.setErrorHandler(dh);
+ reader.setDTDHandler(dh);
+ }
+ reader.parse(is);
+ }
+
+ /**
+ * Returns the {@link org.xml.sax.XMLReader} that is encapsulated by the
+ * implementation of this class.
+ *
+ * @return The XMLReader that is encapsulated by the
+ * implementation of this class.
+ *
+ * @throws SAXException If any SAX errors occur during processing.
+ */
+ public abstract XMLReader getXMLReader() throws SAXException;
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * understand namespaces.
+ *
+ * @return true if this parser is configured to
+ * understand namespaces; false otherwise.
+ */
+ public abstract boolean isNamespaceAware();
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * validate XML documents.
+ *
+ * @return true if this parser is configured to
+ * validate XML documents; false otherwise.
+ */
+ public abstract boolean isValidating();
+
+ /**
+ * <p>Get the XInclude processing mode for this parser.</p>
+ *
+ * @return
+ * the return value of
+ * the {@link SAXParserFactory#isXIncludeAware()}
+ * when this parser was created from factory.
+ *
+ * @throws UnsupportedOperationException When implementation does not
+ * override this method
+ *
+ * @since 1.5
+ *
+ * @see SAXParserFactory#setXIncludeAware(boolean)
+ */
+ public boolean isXIncludeAware() {
+ throw new UnsupportedOperationException(
+ "This parser does not support specification \""
+ + this.getClass().getPackage().getSpecificationTitle()
+ + "\" version \""
+ + this.getClass().getPackage().getSpecificationVersion()
+ + "\"");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/XMLStreamException.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml;
+
+/**
+ * A copy of the StAX XMLStreamException without Location support
+ *
+ * The base exception for unexpected processing errors. This Exception
+ * class is used to report well-formedness errors as well as unexpected
+ * processing conditions.
+ * @version 1.0
+ * @author Copyright (c) 2009 by Oracle Corporation. All Rights Reserved.
+ * @since 1.6
+ */
+
+public class XMLStreamException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+
+ protected Throwable nested;
+
+ /**
+ * Default constructor
+ */
+ public XMLStreamException() {
+ super();
+ }
+
+ /**
+ * Construct an exception with the assocated message.
+ *
+ * @param msg the message to report
+ */
+ public XMLStreamException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Construct an exception with the assocated exception
+ *
+ * @param th a nested exception
+ */
+ public XMLStreamException(Throwable th) {
+ super(th);
+ nested = th;
+ }
+
+ /**
+ * Construct an exception with the assocated message and exception
+ *
+ * @param th a nested exception
+ * @param msg the message to report
+ */
+ public XMLStreamException(String msg, Throwable th) {
+ super(msg, th);
+ nested = th;
+ }
+
+ /**
+ * Gets the nested exception.
+ *
+ * @return Nested exception
+ */
+ public Throwable getNestedException() {
+ return nested;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/XMLStreamWriter.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml;
+
+/**
+ * Basic XMLStreamWriter for writing simple XML files such as those
+ * defined in java.util.Properties
+ *
+ * This is a subset of javax.xml.stream.XMLStreamWriter
+ *
+ * @author Joe Wang
+ */
+public interface XMLStreamWriter {
+
+ //Defaults the XML version to 1.0, and the encoding to utf-8
+ public final static String DEFAULT_XML_VERSION = "1.0";
+ public final static String DEFAULT_ENCODING = "UTF-8";
+
+ /**
+ * Writes a start tag to the output. All writeStartElement methods
+ * open a new scope in the internal namespace context. Writing the
+ * corresponding EndElement causes the scope to be closed.
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeStartElement(String localName) throws XMLStreamException;
+
+ /**
+ * Writes an empty element tag to the output
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeEmptyElement(String localName) throws XMLStreamException;
+
+ /**
+ * Writes an end tag to the output relying on the internal
+ * state of the writer to determine the prefix and local name
+ * of the event.
+ * @throws XMLStreamException
+ */
+ public void writeEndElement() throws XMLStreamException;
+
+ /**
+ * Closes any start tags and writes corresponding end tags.
+ * @throws XMLStreamException
+ */
+ public void writeEndDocument() throws XMLStreamException;
+
+ /**
+ * Close this writer and free any resources associated with the
+ * writer. This must not close the underlying output stream.
+ * @throws XMLStreamException
+ */
+ public void close() throws XMLStreamException;
+
+ /**
+ * Write any cached data to the underlying output mechanism.
+ * @throws XMLStreamException
+ */
+ public void flush() throws XMLStreamException;
+
+ /**
+ * Writes an attribute to the output stream without
+ * a prefix.
+ * @param localName the local name of the attribute
+ * @param value the value of the attribute
+ * @throws IllegalStateException if the current state does not allow Attribute writing
+ * @throws XMLStreamException
+ */
+ public void writeAttribute(String localName, String value)
+ throws XMLStreamException;
+
+ /**
+ * Writes a CData section
+ * @param data the data contained in the CData Section, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeCData(String data) throws XMLStreamException;
+
+ /**
+ * Write a DTD section. This string represents the entire doctypedecl production
+ * from the XML 1.0 specification.
+ *
+ * @param dtd the DTD to be written
+ * @throws XMLStreamException
+ */
+ public void writeDTD(String dtd) throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Defaults the XML version to 1.0, and the encoding to utf-8
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument() throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Defaults the the encoding to utf-8
+ * @param version version of the xml document
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument(String version) throws XMLStreamException;
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does
+ * not set the actual encoding of the underlying output. That must
+ * be set when the instance of the XMLStreamWriter is created using the
+ * XMLOutputFactory
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @throws XMLStreamException If given encoding does not match encoding
+ * of the underlying stream
+ */
+ public void writeStartDocument(String encoding, String version)
+ throws XMLStreamException;
+
+ /**
+ * Write text to the output
+ * @param text the value to write
+ * @throws XMLStreamException
+ */
+ public void writeCharacters(String text) throws XMLStreamException;
+
+ /**
+ * Write text to the output
+ * @param text the value to write
+ * @param start the starting position in the array
+ * @param len the number of characters to write
+ * @throws XMLStreamException
+ */
+ public void writeCharacters(char[] text, int start, int len)
+ throws XMLStreamException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Attrs.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import jdk.internal.org.xml.sax.Attributes;
+
+public class Attrs implements Attributes {
+
+ /**
+ * Attributes string array. Each individual attribute is represented by four
+ * strings: namespace URL(+0), qname(+1), local name(+2), value(+3),
+ * type(+4), declared["d"] and default["D"](+5). In order to find attribute
+ * by the attrubute index, the attribute index MUST be multiplied by 8. The
+ * result will point to the attribute namespace URL.
+ */
+ /* pkg */ String[] mItems;
+ /**
+ * Number of attributes in the attributes string array.
+ */
+ private char mLength;
+ /**
+ * current index
+ */
+ private char mAttrIdx = 0;
+
+ /**
+ * Constructor.
+ */
+ public Attrs() {
+ // The default number of attributies capacity is 8.
+ mItems = new String[(8 << 3)];
+ }
+
+ /**
+ * Sets up the number of attributes and ensure the capacity of the attribute
+ * string array.
+ *
+ * @param length The number of attributes in the object.
+ */
+ public void setLength(char length) {
+ if (length > ((char) (mItems.length >> 3))) {
+ mItems = new String[length << 3];
+ }
+ mLength = length;
+ }
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * <p>Once you know the number of attributes, you can iterate through the
+ * list.</p>
+ *
+ * @return The number of attributes in the list.
+ * @see #getURI(int)
+ * @see #getLocalName(int)
+ * @see #getQName(int)
+ * @see #getType(int)
+ * @see #getValue(int)
+ */
+ public int getLength() {
+ return mLength;
+ }
+
+ /**
+ * Look up an attribute's Namespace URI by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The Namespace URI, or the empty string if none is available, or
+ * null if the index is out of range.
+ * @see #getLength
+ */
+ public String getURI(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[index << 3])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's local name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The local name, or the empty string if Namespace processing is
+ * not being performed, or null if the index is out of range.
+ * @see #getLength
+ */
+ public String getLocalName(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[(index << 3) + 2])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's XML 1.0 qualified name by index.
+ *
+ * @param index The attribute index (zero-based).
+ * @return The XML 1.0 qualified name, or the empty string if none is
+ * available, or null if the index is out of range.
+ * @see #getLength
+ */
+ public String getQName(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ return null;
+ }
+ return mItems[(index << 3) + 1];
+ }
+
+ /**
+ * Look up an attribute's type by index.
+ *
+ * <p>The attribute type is one of the strings "CDATA", "ID", "IDREF",
+ * "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", or "NOTATION"
+ * (always in upper case).</p>
+ *
+ * <p>If the parser has not read a declaration for the attribute, or if the
+ * parser does not report attribute types, then it must return the value
+ * "CDATA" as stated in the XML 1.0 Recommentation (clause 3.3.3,
+ * "Attribute-Value Normalization").</p>
+ *
+ * <p>For an enumerated attribute that is not a notation, the parser will
+ * report the type as "NMTOKEN".</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's type as a string, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public String getType(int index) {
+ return ((index >= 0) && (index < (mItems.length >> 3)))
+ ? (mItems[(index << 3) + 4])
+ : null;
+ }
+
+ /**
+ * Look up an attribute's value by index.
+ *
+ * <p>If the attribute value is a list of tokens (IDREFS, ENTITIES, or
+ * NMTOKENS), the tokens will be concatenated into a single string with each
+ * token separated by a single space.</p>
+ *
+ * @param index The attribute index (zero-based).
+ * @return The attribute's value as a string, or null if the index is out of
+ * range.
+ * @see #getLength
+ */
+ public String getValue(int index) {
+ return ((index >= 0) && (index < mLength))
+ ? (mItems[(index << 3) + 3])
+ : null;
+ }
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ public int getIndex(String uri, String localName) {
+ char len = mLength;
+ for (char idx = 0; idx < len; idx++) {
+ if ((mItems[idx << 3]).equals(uri)
+ && mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up the index of an attribute by Namespace name.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI. <code>null</code> value enforce the search by the local
+ * name only.
+ * @param localName The attribute's local name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ /* pkg */ int getIndexNullNS(String uri, String localName) {
+ char len = mLength;
+ if (uri != null) {
+ for (char idx = 0; idx < len; idx++) {
+ if ((mItems[idx << 3]).equals(uri)
+ && mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ } else {
+ for (char idx = 0; idx < len; idx++) {
+ if (mItems[(idx << 3) + 2].equals(localName)) {
+ return idx;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up the index of an attribute by XML 1.0 qualified name.
+ *
+ * @param qName The qualified (prefixed) name.
+ * @return The index of the attribute, or -1 if it does not appear in the
+ * list.
+ */
+ public int getIndex(String qName) {
+ char len = mLength;
+ for (char idx = 0; idx < len; idx++) {
+ if (mItems[(idx << 3) + 1].equals(qName)) {
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Look up an attribute's type by Namespace name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description of the
+ * possible types.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the name has no
+ * Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute type as a string, or null if the attribute is not
+ * in the list or if Namespace processing is not being performed.
+ */
+ public String getType(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
+ }
+
+ /**
+ * Look up an attribute's type by XML 1.0 qualified name.
+ *
+ * <p>See {@link #getType(int) getType(int)} for a description of the
+ * possible types.</p>
+ *
+ * @param qName The XML 1.0 qualified name.
+ * @return The attribute type as a string, or null if the attribute is not
+ * in the list or if qualified names are not available.
+ */
+ public String getType(String qName) {
+ int idx = getIndex(qName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 4]) : null;
+ }
+
+ /**
+ * Look up an attribute's value by Namespace name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description of the
+ * possible values.</p>
+ *
+ * @param uri The Namespace URI, or the empty String if the name has no
+ * Namespace URI.
+ * @param localName The local name of the attribute.
+ * @return The attribute value as a string, or null if the attribute is not
+ * in the list.
+ */
+ public String getValue(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
+ }
+
+ /**
+ * Look up an attribute's value by XML 1.0 qualified name.
+ *
+ * <p>See {@link #getValue(int) getValue(int)} for a description of the
+ * possible values.</p>
+ *
+ * @param qName The XML 1.0 qualified name.
+ * @return The attribute value as a string, or null if the attribute is not
+ * in the list or if qualified names are not available.
+ */
+ public String getValue(String qName) {
+ int idx = getIndex(qName);
+ return (idx >= 0) ? (mItems[(idx << 3) + 3]) : null;
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * @param index The attribute index (zero-based).
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
+ * index does not identify an attribute.
+ */
+ public boolean isDeclared(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ throw new ArrayIndexOutOfBoundsException("");
+ }
+
+ return ((mItems[(index << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * @param qName The XML qualified (prefixed) name.
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.IllegalArgumentException When the supplied name does
+ * not identify an attribute.
+ */
+ public boolean isDeclared(String qName) {
+ int idx = getIndex(qName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ return ((mItems[(idx << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns false unless the attribute was declared in the DTD. This helps
+ * distinguish two kinds of attributes that SAX reports as CDATA: ones that
+ * were declared (and hence are usually valid), and those that were not (and
+ * which are never valid).
+ *
+ * <p>Remember that since DTDs do not "understand" namespaces, the namespace
+ * URI associated with an attribute may not have come from the DTD. The
+ * declaration will have applied to the attribute's <em>qName</em>.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return true if the attribute was declared in the DTD, false otherwise.
+ * @exception java.lang.IllegalArgumentException When the supplied names do
+ * not identify an attribute.
+ */
+ public boolean isDeclared(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ return ((mItems[(idx << 3) + 5]) != null);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * @param index The attribute index (zero-based).
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.ArrayIndexOutOfBoundsException When the supplied
+ * index does not identify an attribute.
+ */
+ public boolean isSpecified(int index) {
+ if ((index < 0) || (index >= mLength)) {
+ throw new ArrayIndexOutOfBoundsException("");
+ }
+
+ String str = mItems[(index << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * <p>Remember that since DTDs do not "understand" namespaces, the namespace
+ * URI associated with an attribute may not have come from the DTD. The
+ * declaration will have applied to the attribute's <em>qName</em>.
+ *
+ * @param uri The Namespace URI, or the empty string if the name has no
+ * Namespace URI.
+ * @param localName The attribute's local name.
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.IllegalArgumentException When the supplied names do
+ * not identify an attribute.
+ */
+ public boolean isSpecified(String uri, String localName) {
+ int idx = getIndex(uri, localName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ String str = mItems[(idx << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+
+ /**
+ * Returns true unless the attribute value was provided by DTD defaulting.
+ *
+ * @param qName The XML qualified (prefixed) name.
+ * @return true if the value was found in the XML text, false if the value
+ * was provided by DTD defaulting.
+ * @exception java.lang.IllegalArgumentException When the supplied name does
+ * not identify an attribute.
+ */
+ public boolean isSpecified(String qName) {
+ int idx = getIndex(qName);
+ if (idx < 0) {
+ throw new IllegalArgumentException("");
+ }
+
+ String str = mItems[(idx << 3) + 5];
+ return ((str != null) ? (str.charAt(0) == 'd') : true);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Input.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+
+/**
+ * A parsed entity input state.
+ *
+ * This class represents a parsed entity input state. The parser uses
+ * an instance of this class to manage input.
+ */
+
+public class Input {
+
+ /** The entity public identifier or null. */
+ public String pubid;
+ /** The entity systen identifier or null. */
+ public String sysid;
+ /** The encoding from XML declaration or null */
+ public String xmlenc;
+ /** The XML version from XML declaration or 0x0000 */
+ public char xmlver;
+ /** The entity reader. */
+ public Reader src;
+ /** The character buffer. */
+ public char[] chars;
+ /** The length of the character buffer. */
+ public int chLen;
+ /** The index of the next character to read. */
+ public int chIdx;
+ /** The next input in a chain. */
+ public Input next;
+
+ /**
+ * Constructor.
+ *
+ * @param buffsize The input buffer size.
+ */
+ public Input(int buffsize) {
+ chars = new char[buffsize];
+ chLen = chars.length;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param buff The input buffer.
+ */
+ public Input(char[] buff) {
+ chars = buff;
+ chLen = chars.length;
+ }
+
+ /**
+ * Constructor.
+ */
+ public Input() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Pair.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+
+/**
+ * A name with value pair.
+ *
+ * This class keeps name with value pair with additional information and
+ * supports pair chaining.
+ */
+public class Pair {
+
+ /** The pair name. */
+ public String name;
+ /** The pair value. */
+ public String value;
+ /** The pair numeric value. */
+ public int num;
+ /** The characters of name. */
+ public char[] chars;
+ /** The pair identifier. */
+ public int id;
+ /** The list of associated pairs. */
+ public Pair list;
+ /** The next pair in a chain. */
+ public Pair next;
+
+ /**
+ * Creates a qualified name string from qualified name.
+ *
+ * @return The qualified name string.
+ */
+ public String qname() {
+ return new String(chars, 1, chars.length - 1);
+ }
+
+ /**
+ * Creates a local name string from qualified name.
+ *
+ * @return The local name string.
+ */
+ public String local() {
+ if (chars[0] != 0) {
+ return new String(chars, chars[0] + 1, chars.length - chars[0] - 1);
+ }
+ return new String(chars, 1, chars.length - 1);
+ }
+
+ /**
+ * Creates a prefix string from qualified name.
+ *
+ * @return The prefix string.
+ */
+ public String pref() {
+ if (chars[0] != 0) {
+ return new String(chars, 1, chars[0] - 1);
+ }
+ return "";
+ }
+
+ /**
+ * Compares two qualified name prefixes.
+ *
+ * @param qname A qualified name.
+ * @return true if prefixes are equal.
+ */
+ public boolean eqpref(char[] qname) {
+ if (chars[0] == qname[0]) {
+ char len = chars[0];
+ for (char i = 1; i < len; i += 1) {
+ if (chars[i] != qname[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compares two qualified names.
+ *
+ * @param qname A qualified name.
+ * @return true if qualified names are equal.
+ */
+ public boolean eqname(char[] qname) {
+ char len = (char) chars.length;
+ if (len == qname.length) {
+ for (char i = 0; i < len; i += 1) {
+ if (chars[i] != qname[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/Parser.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,3367 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+
+/**
+ * XML non-validating parser engine.
+ */
+public abstract class Parser {
+
+ public final static String FAULT = "";
+ protected final static int BUFFSIZE_READER = 512;
+ protected final static int BUFFSIZE_PARSER = 128;
+ /**
+ * The end of stream character.
+ */
+ public final static char EOS = 0xffff;
+ private Pair mNoNS; // there is no namespace
+ private Pair mXml; // the xml namespace
+ private Map<String, Input> mEnt; // the entities look up table
+ private Map<String, Input> mPEnt; // the parmeter entities look up table
+ protected boolean mIsSAlone; // xml decl standalone flag
+ protected boolean mIsSAloneSet; // standalone is explicitely set
+ protected boolean mIsNSAware; // if true - namespace aware mode
+ protected int mPh; // current phase of document processing
+ protected final static int PH_BEFORE_DOC = -1; // before parsing
+ protected final static int PH_DOC_START = 0; // document start
+ protected final static int PH_MISC_DTD = 1; // misc before DTD
+ protected final static int PH_DTD = 2; // DTD
+ protected final static int PH_DTD_MISC = 3; // misc after DTD
+ protected final static int PH_DOCELM = 4; // document's element
+ protected final static int PH_DOCELM_MISC = 5; // misc after element
+ protected final static int PH_AFTER_DOC = 6; // after parsing
+ protected int mEvt; // current event type
+ protected final static int EV_NULL = 0; // unknown
+ protected final static int EV_ELM = 1; // empty element
+ protected final static int EV_ELMS = 2; // start element
+ protected final static int EV_ELME = 3; // end element
+ protected final static int EV_TEXT = 4; // textual content
+ protected final static int EV_WSPC = 5; // white space content
+ protected final static int EV_PI = 6; // processing instruction
+ protected final static int EV_CDAT = 7; // character data
+ protected final static int EV_COMM = 8; // comment
+ protected final static int EV_DTD = 9; // document type definition
+ protected final static int EV_ENT = 10; // skipped entity
+ private char mESt; // built-in entity recognizer state
+ // mESt values:
+ // 0x100 : the initial state
+ // > 0x100 : unrecognized name
+ // < 0x100 : replacement character
+ protected char[] mBuff; // parser buffer
+ protected int mBuffIdx; // index of the last char
+ protected Pair mPref; // stack of prefixes
+ protected Pair mElm; // stack of elements
+ // mAttL.chars - element qname
+ // mAttL.next - next element
+ // mAttL.list - list of attributes defined on this element
+ // mAttL.list.chars - attribute qname
+ // mAttL.list.id - a char representing attribute's type see below
+ // mAttL.list.next - next attribute defined on the element
+ // mAttL.list.list - devault value structure or null
+ // mAttL.list.list.chars - "name='value' " chars array for Input
+ //
+ // Attribute type character values:
+ // 'i' - "ID"
+ // 'r' - "IDREF"
+ // 'R' - "IDREFS"
+ // 'n' - "ENTITY"
+ // 'N' - "ENTITIES"
+ // 't' - "NMTOKEN"
+ // 'T' - "NMTOKENS"
+ // 'u' - enumeration type
+ // 'o' - "NOTATION"
+ // 'c' - "CDATA"
+ // see also: bkeyword() and atype()
+ //
+ protected Pair mAttL; // list of defined attrs by element name
+ protected Input mDoc; // document entity
+ protected Input mInp; // stack of entities
+ private char[] mChars; // reading buffer
+ private int mChLen; // current capacity
+ private int mChIdx; // index to the next char
+ protected Attrs mAttrs; // attributes of the curr. element
+ private String[] mItems; // attributes array of the curr. element
+ private char mAttrIdx; // attributes counter/index
+ private String mUnent; // unresolved entity name
+ private Pair mDltd; // deleted objects for reuse
+ /**
+ * Default prefixes
+ */
+ private final static char NONS[];
+ private final static char XML[];
+ private final static char XMLNS[];
+
+ static {
+ NONS = new char[1];
+ NONS[0] = (char) 0;
+
+ XML = new char[4];
+ XML[0] = (char) 4;
+ XML[1] = 'x';
+ XML[2] = 'm';
+ XML[3] = 'l';
+
+ XMLNS = new char[6];
+ XMLNS[0] = (char) 6;
+ XMLNS[1] = 'x';
+ XMLNS[2] = 'm';
+ XMLNS[3] = 'l';
+ XMLNS[4] = 'n';
+ XMLNS[5] = 's';
+ }
+ /**
+ * ASCII character type array.
+ *
+ * This array maps an ASCII (7 bit) character to the character type.<br />
+ * Possible character type values are:<br /> - ' ' for any kind of white
+ * space character;<br /> - 'a' for any lower case alphabetical character
+ * value;<br /> - 'A' for any upper case alphabetical character value;<br />
+ * - 'd' for any decimal digit character value;<br /> - 'z' for any
+ * character less then ' ' except '\t', '\n', '\r';<br /> An ASCII (7 bit)
+ * character which does not fall in any category listed above is mapped to
+ * it self.
+ */
+ private static final byte asctyp[];
+ /**
+ * NMTOKEN character type array.
+ *
+ * This array maps an ASCII (7 bit) character to the character type.<br />
+ * Possible character type values are:<br /> - 0 for underscore ('_') or any
+ * lower and upper case alphabetical character value;<br /> - 1 for colon
+ * (':') character;<br /> - 2 for dash ('-') and dot ('.') or any decimal
+ * digit character value;<br /> - 3 for any kind of white space character<br
+ * /> An ASCII (7 bit) character which does not fall in any category listed
+ * above is mapped to 0xff.
+ */
+ private static final byte nmttyp[];
+
+ /**
+ * Static constructor.
+ *
+ * Sets up the ASCII character type array which is used by
+ * {@link #asctyp asctyp} method and NMTOKEN character type array.
+ */
+ static {
+ short i = 0;
+
+ asctyp = new byte[0x80];
+ while (i < ' ') {
+ asctyp[i++] = (byte) 'z';
+ }
+ asctyp['\t'] = (byte) ' ';
+ asctyp['\r'] = (byte) ' ';
+ asctyp['\n'] = (byte) ' ';
+ while (i < '0') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= '9') {
+ asctyp[i++] = (byte) 'd';
+ }
+ while (i < 'A') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= 'Z') {
+ asctyp[i++] = (byte) 'A';
+ }
+ while (i < 'a') {
+ asctyp[i] = (byte) i++;
+ }
+ while (i <= 'z') {
+ asctyp[i++] = (byte) 'a';
+ }
+ while (i < 0x80) {
+ asctyp[i] = (byte) i++;
+ }
+
+ nmttyp = new byte[0x80];
+ for (i = 0; i < '0'; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ while (i <= '9') {
+ nmttyp[i++] = (byte) 2; // digits
+ }
+ while (i < 'A') {
+ nmttyp[i++] = (byte) 0xff;
+ }
+ // skiped upper case alphabetical character are already 0
+ for (i = '['; i < 'a'; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ // skiped lower case alphabetical character are already 0
+ for (i = '{'; i < 0x80; i++) {
+ nmttyp[i] = (byte) 0xff;
+ }
+ nmttyp['_'] = 0;
+ nmttyp[':'] = 1;
+ nmttyp['.'] = 2;
+ nmttyp['-'] = 2;
+ nmttyp[' '] = 3;
+ nmttyp['\t'] = 3;
+ nmttyp['\r'] = 3;
+ nmttyp['\n'] = 3;
+ }
+
+ /**
+ * Constructor.
+ */
+ protected Parser() {
+ mPh = PH_BEFORE_DOC; // before parsing
+
+ // Initialize the parser
+ mBuff = new char[BUFFSIZE_PARSER];
+ mAttrs = new Attrs();
+
+ // Default namespace
+ mPref = pair(mPref);
+ mPref.name = "";
+ mPref.value = "";
+ mPref.chars = NONS;
+ mNoNS = mPref; // no namespace
+ // XML namespace
+ mPref = pair(mPref);
+ mPref.name = "xml";
+ mPref.value = "http://www.w3.org/XML/1998/namespace";
+ mPref.chars = XML;
+ mXml = mPref; // XML namespace
+ }
+
+ /**
+ * Initializes parser's internals. Note, current input has to be set before
+ * this method is called.
+ */
+ protected void init() {
+ mUnent = null;
+ mElm = null;
+ mPref = mXml;
+ mAttL = null;
+ mPEnt = new HashMap<>();
+ mEnt = new HashMap<>();
+ mDoc = mInp; // current input is document entity
+ mChars = mInp.chars; // use document entity buffer
+ mPh = PH_DOC_START; // the begining of the document
+ }
+
+ /**
+ * Cleans up parser internal resources.
+ */
+ protected void cleanup() {
+ // Default attributes
+ while (mAttL != null) {
+ while (mAttL.list != null) {
+ if (mAttL.list.list != null) {
+ del(mAttL.list.list);
+ }
+ mAttL.list = del(mAttL.list);
+ }
+ mAttL = del(mAttL);
+ }
+ // Element stack
+ while (mElm != null) {
+ mElm = del(mElm);
+ }
+ // Namespace prefixes
+ while (mPref != mXml) {
+ mPref = del(mPref);
+ }
+ // Inputs
+ while (mInp != null) {
+ pop();
+ }
+ // Document reader
+ if ((mDoc != null) && (mDoc.src != null)) {
+ try {
+ mDoc.src.close();
+ } catch (IOException ioe) {
+ }
+ }
+ mPEnt = null;
+ mEnt = null;
+ mDoc = null;
+ mPh = PH_AFTER_DOC; // before documnet processing
+ }
+
+ /**
+ * Processes a portion of document. This method returns one of EV_*
+ * constants as an identifier of the portion of document have been read.
+ *
+ * @return Identifier of processed document portion.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ protected int step() throws Exception {
+ mEvt = EV_NULL;
+ int st = 0;
+ while (mEvt == EV_NULL) {
+ char ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // all sorts of markup (dispetcher)
+ if (ch != '<') {
+ bkch();
+ mBuffIdx = -1; // clean parser buffer
+ st = 1;
+ break;
+ }
+ switch (getch()) {
+ case '/': // the end of the element content
+ mEvt = EV_ELME;
+ if (mElm == null) {
+ panic(FAULT);
+ }
+ // Check element's open/close tags balance
+ mBuffIdx = -1; // clean parser buffer
+ bname(mIsNSAware);
+ char[] chars = mElm.chars;
+ if (chars.length == (mBuffIdx + 1)) {
+ for (char i = 1; i <= mBuffIdx; i += 1) {
+ if (chars[i] != mBuff[i]) {
+ panic(FAULT);
+ }
+ }
+ } else {
+ panic(FAULT);
+ }
+ // Skip white spaces before '>'
+ if (wsskip() != '>') {
+ panic(FAULT);
+ }
+ getch(); // read '>'
+ break;
+
+ case '!': // a comment or a CDATA
+ ch = getch();
+ bkch();
+ switch (ch) {
+ case '-': // must be a comment
+ mEvt = EV_COMM;
+ comm();
+ break;
+
+ case '[': // must be a CDATA section
+ mEvt = EV_CDAT;
+ cdat();
+ break;
+
+ default: // must be 'DOCTYPE'
+ mEvt = EV_DTD;
+ dtd();
+ break;
+ }
+ break;
+
+ case '?': // processing instruction
+ mEvt = EV_PI;
+ pi();
+ break;
+
+ default: // must be the first char of an xml name
+ bkch();
+ // Read an element name and put it on top of the
+ // element stack
+ mElm = pair(mElm); // add new element to the stack
+ mElm.chars = qname(mIsNSAware);
+ mElm.name = mElm.local();
+ mElm.id = (mElm.next != null) ? mElm.next.id : 0; // flags
+ mElm.num = 0; // namespace counter
+ // Find the list of defined attributs of the current
+ // element
+ Pair elm = find(mAttL, mElm.chars);
+ mElm.list = (elm != null) ? elm.list : null;
+ // Read attributes till the end of the element tag
+ mAttrIdx = 0;
+ Pair att = pair(null);
+ att.num = 0; // clear attribute's flags
+ attr(att); // get all attributes inc. defaults
+ del(att);
+ mElm.value = (mIsNSAware) ? rslv(mElm.chars) : null;
+ // Skip white spaces before '>'
+ switch (wsskip()) {
+ case '>':
+ getch(); // read '>'
+ mEvt = EV_ELMS;
+ break;
+
+ case '/':
+ getch(); // read '/'
+ if (getch() != '>') // read '>'
+ {
+ panic(FAULT);
+ }
+ mEvt = EV_ELM;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+ }
+ break;
+
+ case 1: // read white space
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ bappend(ch);
+ break;
+
+ case '\r': // EOL processing [#2.11]
+ if (getch() != '\n') {
+ bkch();
+ }
+ bappend('\n');
+ break;
+
+ case '<':
+ mEvt = EV_WSPC;
+ bkch();
+ bflash_ws();
+ break;
+
+ default:
+ bkch();
+ st = 2;
+ break;
+ }
+ break;
+
+ case 2: // read the text content of the element
+ switch (ch) {
+ case '&':
+ if (mUnent == null) {
+ // There was no unresolved entity on previous step.
+ if ((mUnent = ent('x')) != null) {
+ mEvt = EV_TEXT;
+ bkch(); // move back to ';' after entity name
+ setch('&'); // parser must be back on next step
+ bflash();
+ }
+ } else {
+ // There was unresolved entity on previous step.
+ mEvt = EV_ENT;
+ skippedEnt(mUnent);
+ mUnent = null;
+ }
+ break;
+
+ case '<':
+ mEvt = EV_TEXT;
+ bkch();
+ bflash();
+ break;
+
+ case '\r': // EOL processing [#2.11]
+ if (getch() != '\n') {
+ bkch();
+ }
+ bappend('\n');
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+
+ return mEvt;
+ }
+
+ /**
+ * Parses the document type declaration.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtd() throws Exception {
+ char ch;
+ String str = null;
+ String name = null;
+ Pair psid = null;
+ // read 'DOCTYPE'
+ if ("DOCTYPE".equals(name(false)) != true) {
+ panic(FAULT);
+ }
+ mPh = PH_DTD; // DTD
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // read the document type name
+ if (chtyp(ch) != ' ') {
+ bkch();
+ name = name(mIsNSAware);
+ wsskip();
+ st = 1; // read 'PUPLIC' or 'SYSTEM'
+ }
+ break;
+
+ case 1: // read 'PUPLIC' or 'SYSTEM'
+ switch (chtyp(ch)) {
+ case 'A':
+ bkch();
+ psid = pubsys(' ');
+ st = 2; // skip spaces before internal subset
+ docType(name, psid.name, psid.value);
+ break;
+
+ case '[':
+ bkch();
+ st = 2; // skip spaces before internal subset
+ docType(name, null, null);
+ break;
+
+ case '>':
+ bkch();
+ st = 3; // skip spaces after internal subset
+ docType(name, null, null);
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // skip spaces before internal subset
+ switch (chtyp(ch)) {
+ case '[':
+ // Process internal subset
+ dtdsub();
+ st = 3; // skip spaces after internal subset
+ break;
+
+ case '>':
+ // There is no internal subset
+ bkch();
+ st = 3; // skip spaces after internal subset
+ break;
+
+ case ' ':
+ // skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 3: // skip spaces after internal subset
+ switch (chtyp(ch)) {
+ case '>':
+ if (psid != null) {
+ // Report the DTD external subset
+ InputSource is = resolveEnt(name, psid.name, psid.value);
+ if (is != null) {
+ if (mIsSAlone == false) {
+ // Set the end of DTD external subset char
+ bkch();
+ setch(']');
+ // Set the DTD external subset InputSource
+ push(new Input(BUFFSIZE_READER));
+ setinp(is);
+ mInp.pubid = psid.name;
+ mInp.sysid = psid.value;
+ // Parse the DTD external subset
+ dtdsub();
+ } else {
+ // Unresolved DTD external subset
+ skippedEnt("[dtd]");
+ // Release reader and stream
+ if (is.getCharacterStream() != null) {
+ try {
+ is.getCharacterStream().close();
+ } catch (IOException ioe) {
+ }
+ }
+ if (is.getByteStream() != null) {
+ try {
+ is.getByteStream().close();
+ } catch (IOException ioe) {
+ }
+ }
+ }
+ } else {
+ // Unresolved DTD external subset
+ skippedEnt("[dtd]");
+ }
+ del(psid);
+ }
+ st = -1; // end of DTD
+ break;
+
+ case ' ':
+ // skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses the document type declaration subset.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdsub() throws Exception {
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip white spaces before a declaration
+ switch (chtyp(ch)) {
+ case '<':
+ ch = getch();
+ switch (ch) {
+ case '?':
+ pi();
+ break;
+
+ case '!':
+ ch = getch();
+ bkch();
+ if (ch == '-') {
+ comm();
+ break;
+ }
+ // A markup or an entity declaration
+ bntok();
+ switch (bkeyword()) {
+ case 'n':
+ dtdent();
+ break;
+
+ case 'a':
+ dtdattl(); // parse attributes declaration
+ break;
+
+ case 'e':
+ dtdelm(); // parse element declaration
+ break;
+
+ case 'o':
+ dtdnot(); // parse notation declaration
+ break;
+
+ default:
+ panic(FAULT); // unsupported markup declaration
+ break;
+ }
+ st = 1; // read the end of declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case '%':
+ // A parameter entity reference
+ pent(' ');
+ break;
+
+ case ']':
+ // End of DTD subset
+ st = -1;
+ break;
+
+ case ' ':
+ // Skip white spaces
+ break;
+
+ case 'Z':
+ // End of stream
+ if (getch() != ']') {
+ panic(FAULT);
+ }
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read the end of declaration
+ switch (ch) {
+ case '>': // there is no notation
+ st = 0; // skip white spaces before a declaration
+ break;
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ // Skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses an entity declaration. This method fills the general (
+ * <code>mEnt</code>) and parameter
+ * (
+ * <code>mPEnt</code>) entity look up table.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdent() throws Exception {
+ String str = null;
+ char[] val = null;
+ Input inp = null;
+ Pair ids = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip white spaces before entity name
+ switch (chtyp(ch)) {
+ case ' ':
+ // Skip white spaces
+ break;
+
+ case '%':
+ // Parameter entity or parameter entity declaration.
+ ch = getch();
+ bkch();
+ if (chtyp(ch) == ' ') {
+ // Parameter entity declaration.
+ wsskip();
+ str = name(false);
+ switch (chtyp(wsskip())) {
+ case 'A':
+ // Read the external identifier
+ ids = pubsys(' ');
+ if (wsskip() == '>') {
+ // External parsed entity
+ if (mPEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input();
+ inp.pubid = ids.name;
+ inp.sysid = ids.value;
+ mPEnt.put(str, inp);
+ }
+ } else {
+ panic(FAULT);
+ }
+ del(ids);
+ st = -1; // the end of declaration
+ break;
+
+ case '\"':
+ case '\'':
+ // Read the parameter entity value
+ bqstr('d');
+ // Create the parameter entity value
+ val = new char[mBuffIdx + 1];
+ System.arraycopy(mBuff, 1, val, 1, val.length - 1);
+ // Add surrounding spaces [#4.4.8]
+ val[0] = ' ';
+ // Add the entity to the entity look up table
+ if (mPEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input(val);
+ inp.pubid = mInp.pubid;
+ inp.sysid = mInp.sysid;
+ inp.xmlenc = mInp.xmlenc;
+ inp.xmlver = mInp.xmlver;
+ mPEnt.put(str, inp);
+ }
+ st = -1; // the end of declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ } else {
+ // Parameter entity reference.
+ pent(' ');
+ }
+ break;
+
+ default:
+ bkch();
+ str = name(false);
+ st = 1; // read entity declaration value
+ break;
+ }
+ break;
+
+ case 1: // read entity declaration value
+ switch (chtyp(ch)) {
+ case '\"': // internal entity
+ case '\'':
+ bkch();
+ bqstr('d'); // read a string into the buffer
+ if (mEnt.get(str) == null) {
+ // Create general entity value
+ val = new char[mBuffIdx];
+ System.arraycopy(mBuff, 1, val, 0, val.length);
+ // Add the entity to the entity look up table
+ if (mEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input(val);
+ inp.pubid = mInp.pubid;
+ inp.sysid = mInp.sysid;
+ inp.xmlenc = mInp.xmlenc;
+ inp.xmlver = mInp.xmlver;
+ mEnt.put(str, inp);
+ }
+ }
+ st = -1; // the end of declaration
+ break;
+
+ case 'A': // external entity
+ bkch();
+ ids = pubsys(' ');
+ switch (wsskip()) {
+ case '>': // external parsed entity
+ if (mEnt.containsKey(str) == false) { // [#4.2]
+ inp = new Input();
+ inp.pubid = ids.name;
+ inp.sysid = ids.value;
+ mEnt.put(str, inp);
+ }
+ break;
+
+ case 'N': // external general unparsed entity
+ if ("NDATA".equals(name(false)) == true) {
+ wsskip();
+ unparsedEntDecl(str, ids.name, ids.value, name(false));
+ break;
+ }
+ default:
+ panic(FAULT);
+ break;
+ }
+ del(ids);
+ st = -1; // the end of declaration
+ break;
+
+ case ' ':
+ // Skip white spaces
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses an element declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdelm() throws Exception {
+ // This is stub implementation which skips an element
+ // declaration.
+ wsskip();
+ name(mIsNSAware);
+
+ char ch;
+ while (true) {
+ ch = getch();
+ switch (ch) {
+ case '>':
+ bkch();
+ return;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute list declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdattl() throws Exception {
+ char elmqn[] = null;
+ Pair elm = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // read the element name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ // Get the element from the list or add a new one.
+ elmqn = qname(mIsNSAware);
+ elm = find(mAttL, elmqn);
+ if (elm == null) {
+ elm = pair(mAttL);
+ elm.chars = elmqn;
+ mAttL = elm;
+ }
+ st = 1; // read an attribute declaration
+ break;
+
+ case ' ':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 1: // read an attribute declaration
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ dtdatt(elm);
+ if (wsskip() == '>') {
+ return;
+ }
+ break;
+
+ case ' ':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses an attribute declaration.
+ *
+ * The attribute uses the following fields of Pair object: chars - characters
+ * of qualified name id - the type identifier of the attribute list - a pair
+ * which holds the default value (chars field)
+ *
+ * @param elm An object which represents all defined attributes on an
+ * element.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void dtdatt(Pair elm) throws Exception {
+ char attqn[] = null;
+ Pair att = null;
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // the attribute name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ case ':':
+ bkch();
+ // Get the attribut from the list or add a new one.
+ attqn = qname(mIsNSAware);
+ att = find(elm.list, attqn);
+ if (att == null) {
+ // New attribute declaration
+ att = pair(elm.list);
+ att.chars = attqn;
+ elm.list = att;
+ } else {
+ // Do not override the attribute declaration [#3.3]
+ att = pair(null);
+ att.chars = attqn;
+ att.id = 'c';
+ }
+ wsskip();
+ st = 1;
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 1: // the attribute type
+ switch (chtyp(ch)) {
+ case '(':
+ att.id = 'u'; // enumeration type
+ st = 2; // read the first element of the list
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ bkch();
+ bntok(); // read type id
+ att.id = bkeyword();
+ switch (att.id) {
+ case 'o': // NOTATION
+ if (wsskip() != '(') {
+ panic(FAULT);
+ }
+ ch = getch();
+ st = 2; // read the first element of the list
+ break;
+
+ case 'i': // ID
+ case 'r': // IDREF
+ case 'R': // IDREFS
+ case 'n': // ENTITY
+ case 'N': // ENTITIES
+ case 't': // NMTOKEN
+ case 'T': // NMTOKENS
+ case 'c': // CDATA
+ wsskip();
+ st = 4; // read default declaration
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 2: // read the first element of the list
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case 'd':
+ case '.':
+ case ':':
+ case '-':
+ case '_':
+ case 'X':
+ bkch();
+ switch (att.id) {
+ case 'u': // enumeration type
+ bntok();
+ break;
+
+ case 'o': // NOTATION
+ mBuffIdx = -1;
+ bname(false);
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ wsskip();
+ st = 3; // read next element of the list
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 3: // read next element of the list
+ switch (ch) {
+ case ')':
+ wsskip();
+ st = 4; // read default declaration
+ break;
+
+ case '|':
+ wsskip();
+ switch (att.id) {
+ case 'u': // enumeration type
+ bntok();
+ break;
+
+ case 'o': // NOTATION
+ mBuffIdx = -1;
+ bname(false);
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ wsskip();
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 4: // read default declaration
+ switch (ch) {
+ case '#':
+ bntok();
+ switch (bkeyword()) {
+ case 'F': // FIXED
+ switch (wsskip()) {
+ case '\"':
+ case '\'':
+ st = 5; // read the default value
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 'Q': // REQUIRED
+ case 'I': // IMPLIED
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case '\"':
+ case '\'':
+ bkch();
+ st = 5; // read the default value
+ break;
+
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ break;
+
+ case '%':
+ pent(' ');
+ break;
+
+ default:
+ bkch();
+ st = -1;
+ break;
+ }
+ break;
+
+ case 5: // read the default value
+ switch (ch) {
+ case '\"':
+ case '\'':
+ bkch();
+ bqstr('d'); // the value in the mBuff now
+ att.list = pair(null);
+ // Create a string like "attqname='value' "
+ att.list.chars = new char[att.chars.length + mBuffIdx + 3];
+ System.arraycopy(
+ att.chars, 1, att.list.chars, 0, att.chars.length - 1);
+ att.list.chars[att.chars.length - 1] = '=';
+ att.list.chars[att.chars.length] = ch;
+ System.arraycopy(
+ mBuff, 1, att.list.chars, att.chars.length + 1, mBuffIdx);
+ att.list.chars[att.chars.length + mBuffIdx + 1] = ch;
+ att.list.chars[att.chars.length + mBuffIdx + 2] = ' ';
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses a notation declaration.
+ *
+ * This method parses the declaration up to the closing angle bracket.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void dtdnot() throws Exception {
+ wsskip();
+ String name = name(false);
+ wsskip();
+ Pair ids = pubsys('N');
+ notDecl(name, ids.name, ids.value);
+ del(ids);
+ }
+
+ /**
+ * Parses an attribute.
+ *
+ * This recursive method is responsible for prefix addition
+ * (
+ * <code>mPref</code>) on the way down. The element's start tag end triggers
+ * the return process. The method then on it's way back resolves prefixes
+ * and accumulates attributes.
+ *
+ * <p><code>att.num</code> carries attribute flags where: 0x1 - attribute is
+ * declared in DTD (attribute decalration had been read); 0x2 - attribute's
+ * default value is used.</p>
+ *
+ * @param att An object which reprecents current attribute.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void attr(Pair att) throws Exception {
+ switch (wsskip()) {
+ case '/':
+ case '>':
+ if ((att.num & 0x2) == 0) { // all attributes have been read
+ att.num |= 0x2; // set default attribute flag
+ Input inp = mInp;
+ // Go through all attributes defined on current element.
+ for (Pair def = mElm.list; def != null; def = def.next) {
+ if (def.list == null) // no default value
+ {
+ continue;
+ }
+ // Go through all attributes defined on current
+ // element and add defaults.
+ Pair act = find(att.next, def.chars);
+ if (act == null) {
+ push(new Input(def.list.chars));
+ }
+ }
+ if (mInp != inp) { // defaults have been added
+ attr(att);
+ return;
+ }
+ }
+ // Ensure the attribute string array capacity
+ mAttrs.setLength(mAttrIdx);
+ mItems = mAttrs.mItems;
+ return;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ // Read the attribute name and value
+ att.chars = qname(mIsNSAware);
+ att.name = att.local();
+ String type = atype(att); // sets attribute's type on att.id
+ wsskip();
+ if (getch() != '=') {
+ panic(FAULT);
+ }
+ bqstr((char) att.id); // read the value with normalization.
+ String val = new String(mBuff, 1, mBuffIdx);
+ Pair next = pair(att);
+ next.num = (att.num & ~0x1); // inherit attribute flags
+ // Put a namespace declaration on top of the prefix stack
+ if ((mIsNSAware == false) || (isdecl(att, val) == false)) {
+ // An ordinary attribute
+ mAttrIdx++;
+ attr(next); // recursive call to parse the next attribute
+ mAttrIdx--;
+ // Add the attribute to the attributes string array
+ char idx = (char) (mAttrIdx << 3);
+ mItems[idx + 1] = att.qname(); // attr qname
+ mItems[idx + 2] = (mIsNSAware) ? att.name : ""; // attr local name
+ mItems[idx + 3] = val; // attr value
+ mItems[idx + 4] = type; // attr type
+ switch (att.num & 0x3) {
+ case 0x0:
+ mItems[idx + 5] = null;
+ break;
+
+ case 0x1: // declared attribute
+ mItems[idx + 5] = "d";
+ break;
+
+ default: // 0x2, 0x3 - default attribute always declared
+ mItems[idx + 5] = "D";
+ break;
+ }
+ // Resolve the prefix if any and report the attribute
+ // NOTE: The attribute does not accept the default namespace.
+ mItems[idx + 0] = (att.chars[0] != 0) ? rslv(att.chars) : "";
+ } else {
+ // A namespace declaration. mPref.name contains prefix and
+ // mPref.value contains namespace URI set by isdecl method.
+ // Report a start of the new mapping
+ newPrefix();
+ // Recursive call to parse the next attribute
+ attr(next);
+ // NOTE: The namespace declaration is not reported.
+ }
+ del(next);
+ break;
+ }
+ }
+
+ /**
+ * Retrieves attribute type.
+ *
+ * This method sets the type of normalization in the attribute
+ * <code>id</code> field and returns the name of attribute type.
+ *
+ * @param att An object which represents current attribute.
+ * @return The name of the attribute type.
+ * @exception Exception is parser specific exception form panic method.
+ */
+ private String atype(Pair att)
+ throws Exception {
+ Pair attr;
+
+ // CDATA-type normalization by default [#3.3.3]
+ att.id = 'c';
+ if (mElm.list == null || (attr = find(mElm.list, att.chars)) == null) {
+ return "CDATA";
+ }
+
+ att.num |= 0x1; // attribute is declared
+
+ // Non-CDATA normalization except when the attribute type is CDATA.
+ att.id = 'i';
+ switch (attr.id) {
+ case 'i':
+ return "ID";
+
+ case 'r':
+ return "IDREF";
+
+ case 'R':
+ return "IDREFS";
+
+ case 'n':
+ return "ENTITY";
+
+ case 'N':
+ return "ENTITIES";
+
+ case 't':
+ return "NMTOKEN";
+
+ case 'T':
+ return "NMTOKENS";
+
+ case 'u':
+ return "NMTOKEN";
+
+ case 'o':
+ return "NOTATION";
+
+ case 'c':
+ att.id = 'c';
+ return "CDATA";
+
+ default:
+ panic(FAULT);
+ }
+ return null;
+ }
+
+ /**
+ * Parses a comment.
+ *
+ * The '<!' part is read in dispatcher so the method starts
+ * with first '-' after '<!'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ */
+ @SuppressWarnings("fallthrough")
+ private void comm() throws Exception {
+ if (mPh == PH_DOC_START) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ } // '<!' has been already read by dispetcher.
+ char ch;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ if (ch == EOS) {
+ panic(FAULT);
+ }
+ switch (st) {
+ case 0: // first '-' of the comment open
+ if (ch == '-') {
+ st = 1;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // secind '-' of the comment open
+ if (ch == '-') {
+ st = 2;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // skip the comment body
+ switch (ch) {
+ case '-':
+ st = 3;
+ break;
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ case 3: // second '-' of the comment close
+ switch (ch) {
+ case '-':
+ st = 4;
+ break;
+
+ default:
+ bappend('-');
+ bappend(ch);
+ st = 2;
+ break;
+ }
+ break;
+
+ case 4: // '>' of the comment close
+ if (ch == '>') {
+ comm(mBuff, mBuffIdx + 1);
+ st = -1;
+ break;
+ }
+ // else - panic [#2.5 compatibility note]
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses a processing instruction.
+ *
+ * The '<?' is read in dispatcher so the method starts with
+ * first character of PI target name after '<?'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void pi() throws Exception {
+ // '<?' has been already read by dispetcher.
+ char ch;
+ String str = null;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ if (ch == EOS) {
+ panic(FAULT);
+ }
+ switch (st) {
+ case 0: // read the PI target name
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ case ':':
+ case 'X':
+ bkch();
+ str = name(false);
+ // PI target name may not be empty string [#2.6]
+ // PI target name 'XML' is reserved [#2.6]
+ if ((str.length() == 0)
+ || (mXml.name.equals(str.toLowerCase()) == true)) {
+ panic(FAULT);
+ }
+ // This is processing instruction
+ if (mPh == PH_DOC_START) // the begining of the document
+ {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ wsskip(); // skip spaces after the PI target name
+ st = 1; // accumulate the PI body
+ mBuffIdx = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // accumulate the PI body
+ switch (ch) {
+ case '?':
+ st = 2; // end of the PI body
+ break;
+
+ default:
+ bappend(ch);
+ break;
+ }
+ break;
+
+ case 2: // end of the PI body
+ switch (ch) {
+ case '>':
+ // PI has been read.
+ pi(str, new String(mBuff, 0, mBuffIdx + 1));
+ st = -1;
+ break;
+
+ case '?':
+ bappend('?');
+ break;
+
+ default:
+ bappend('?');
+ bappend(ch);
+ st = 1; // accumulate the PI body
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Parses a character data.
+ *
+ * The '<!' part is read in dispatcher so the method starts
+ * with first '[' after '<!'.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void cdat()
+ throws Exception {
+ // '<!' has been already read by dispetcher.
+ char ch;
+ mBuffIdx = -1;
+ for (short st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // the first '[' of the CDATA open
+ if (ch == '[') {
+ st = 1;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read "CDATA"
+ if (chtyp(ch) == 'A') {
+ bappend(ch);
+ } else {
+ if ("CDATA".equals(
+ new String(mBuff, 0, mBuffIdx + 1)) != true) {
+ panic(FAULT);
+ }
+ bkch();
+ st = 2;
+ }
+ break;
+
+ case 2: // the second '[' of the CDATA open
+ if (ch != '[') {
+ panic(FAULT);
+ }
+ mBuffIdx = -1;
+ st = 3;
+ break;
+
+ case 3: // read data before the first ']'
+ if (ch != ']') {
+ bappend(ch);
+ } else {
+ st = 4;
+ }
+ break;
+
+ case 4: // read the second ']' or continue to read the data
+ if (ch != ']') {
+ bappend(']');
+ bappend(ch);
+ st = 3;
+ } else {
+ st = 5;
+ }
+ break;
+
+ case 5: // read '>' or continue to read the data
+ switch (ch) {
+ case ']':
+ bappend(']');
+ break;
+
+ case '>':
+ bflash();
+ st = -1;
+ break;
+
+ default:
+ bappend(']');
+ bappend(']');
+ bappend(ch);
+ st = 3;
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Reads a xml name.
+ *
+ * The xml name must conform "Namespaces in XML" specification. Therefore
+ * the ':' character is not allowed in the name. This method should be used
+ * for PI and entity names which may not have a namespace according to the
+ * specification mentioned above.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @return The name has been read.
+ * @exception Exception When incorrect character appear in the name.
+ * @exception IOException
+ */
+ protected String name(boolean ns)
+ throws Exception {
+ mBuffIdx = -1;
+ bname(ns);
+ return new String(mBuff, 1, mBuffIdx);
+ }
+
+ /**
+ * Reads a qualified xml name.
+ *
+ * The characters of a qualified name is an array of characters. The first
+ * (chars[0]) character is the index of the colon character which separates
+ * the prefix from the local name. If the index is zero, the name does not
+ * contain separator or the parser works in the namespace unaware mode. The
+ * length of qualified name is the length of the array minus one.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @return The characters of a qualified name.
+ * @exception Exception When incorrect character appear in the name.
+ * @exception IOException
+ */
+ protected char[] qname(boolean ns)
+ throws Exception {
+ mBuffIdx = -1;
+ bname(ns);
+ char chars[] = new char[mBuffIdx + 1];
+ System.arraycopy(mBuff, 0, chars, 0, mBuffIdx + 1);
+ return chars;
+ }
+
+ /**
+ * Reads the public or/and system identifiers.
+ *
+ * @param inp The input object.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void pubsys(Input inp)
+ throws Exception {
+ Pair pair = pubsys(' ');
+ inp.pubid = pair.name;
+ inp.sysid = pair.value;
+ del(pair);
+ }
+
+ /**
+ * Reads the public or/and system identifiers.
+ *
+ * @param flag The 'N' allows public id be without system id.
+ * @return The public or/and system identifiers pair.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private Pair pubsys(char flag) throws Exception {
+ Pair ids = pair(null);
+ String str = name(false);
+ if ("PUBLIC".equals(str) == true) {
+ bqstr('i'); // non-CDATA normalization [#4.2.2]
+ ids.name = new String(mBuff, 1, mBuffIdx);
+ switch (wsskip()) {
+ case '\"':
+ case '\'':
+ bqstr(' ');
+ ids.value = new String(mBuff, 1, mBuffIdx);
+ break;
+
+ case EOS:
+ panic(FAULT);
+
+ default:
+ if (flag != 'N') // [#4.7]
+ {
+ panic(FAULT);
+ }
+ ids.value = null;
+ break;
+ }
+ return ids;
+ } else if ("SYSTEM".equals(str) == true) {
+ ids.name = null;
+ bqstr(' ');
+ ids.value = new String(mBuff, 1, mBuffIdx);
+ return ids;
+ }
+ panic(FAULT);
+ return null;
+ }
+
+ /**
+ * Reads an attribute value.
+ *
+ * The grammar which this method can read is:<br />
+ * <code>eqstr := S "=" qstr</code><br />
+ * <code>qstr := S ("'" string "'") |
+ * ('"' string '"')</code><br /> This method resolves entities
+ * inside a string unless the parser parses DTD.
+ *
+ * @param flag The '=' character forces the method to accept the '='
+ * character before quoted string and read the following string as not an
+ * attribute ('-'), 'c' - CDATA, 'i' - non CDATA, ' ' - no normalization;
+ * '-' - not an attribute value; 'd' - in DTD context.
+ * @return The content of the quoted strign as a string.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ protected String eqstr(char flag) throws Exception {
+ if (flag == '=') {
+ wsskip();
+ if (getch() != '=') {
+ panic(FAULT);
+ }
+ }
+ bqstr((flag == '=') ? '-' : flag);
+ return new String(mBuff, 1, mBuffIdx);
+ }
+
+ /**
+ * Resoves an entity.
+ *
+ * This method resolves built-in and character entity references. It is also
+ * reports external entities to the application.
+ *
+ * @param flag The 'x' character forces the method to report a skipped
+ * entity; 'i' character - indicates non-CDATA normalization.
+ * @return Name of unresolved entity or <code>null</code> if entity had been
+ * resolved successfully.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private String ent(char flag) throws Exception {
+ char ch;
+ int idx = mBuffIdx + 1;
+ Input inp = null;
+ String str = null;
+ mESt = 0x100; // reset the built-in entity recognizer
+ bappend('&');
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // the first character of the entity name
+ case 1: // read built-in entity name
+ switch (chtyp(ch)) {
+ case 'd':
+ case '.':
+ case '-':
+ if (st != 1) {
+ panic(FAULT);
+ }
+ case 'a':
+ case 'A':
+ case '_':
+ case 'X':
+ bappend(ch);
+ eappend(ch);
+ st = 1;
+ break;
+
+ case ':':
+ if (mIsNSAware != false) {
+ panic(FAULT);
+ }
+ bappend(ch);
+ eappend(ch);
+ st = 1;
+ break;
+
+ case ';':
+ if (mESt < 0x100) {
+ // The entity is a built-in entity
+ mBuffIdx = idx - 1;
+ bappend(mESt);
+ st = -1;
+ break;
+ } else if (mPh == PH_DTD) {
+ // In DTD entity declaration has to resolve character
+ // entities and include "as is" others. [#4.4.7]
+ bappend(';');
+ st = -1;
+ break;
+ }
+ // Convert an entity name to a string
+ str = new String(mBuff, idx + 1, mBuffIdx - idx);
+ inp = mEnt.get(str);
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (inp != null) {
+ if (inp.chars == null) {
+ // External entity
+ InputSource is = resolveEnt(str, inp.pubid, inp.sysid);
+ if (is != null) {
+ push(new Input(BUFFSIZE_READER));
+ setinp(is);
+ mInp.pubid = inp.pubid;
+ mInp.sysid = inp.sysid;
+ str = null; // the entity is resolved
+ } else {
+ // Unresolved external entity
+ if (flag != 'x') {
+ panic(FAULT); // unknown entity within marckup
+ } // str is name of unresolved entity
+ }
+ } else {
+ // Internal entity
+ push(inp);
+ str = null; // the entity is resolved
+ }
+ } else {
+ // Unknown or general unparsed entity
+ if (flag != 'x') {
+ panic(FAULT); // unknown entity within marckup
+ } // str is name of unresolved entity
+ }
+ st = -1;
+ break;
+
+ case '#':
+ if (st != 0) {
+ panic(FAULT);
+ }
+ st = 2;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 2: // read character entity
+ switch (chtyp(ch)) {
+ case 'd':
+ bappend(ch);
+ break;
+
+ case ';':
+ // Convert the character entity to a character
+ try {
+ int i = Integer.parseInt(
+ new String(mBuff, idx + 1, mBuffIdx - idx), 10);
+ if (i >= 0xffff) {
+ panic(FAULT);
+ }
+ ch = (char) i;
+ } catch (NumberFormatException nfe) {
+ panic(FAULT);
+ }
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (ch == ' ' || mInp.next != null) {
+ bappend(ch, flag);
+ } else {
+ bappend(ch);
+ }
+ st = -1;
+ break;
+
+ case 'a':
+ // If the entity buffer is empty and ch == 'x'
+ if ((mBuffIdx == idx) && (ch == 'x')) {
+ st = 3;
+ break;
+ }
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 3: // read hex character entity
+ switch (chtyp(ch)) {
+ case 'A':
+ case 'a':
+ case 'd':
+ bappend(ch);
+ break;
+
+ case ';':
+ // Convert the character entity to a character
+ try {
+ int i = Integer.parseInt(
+ new String(mBuff, idx + 1, mBuffIdx - idx), 16);
+ if (i >= 0xffff) {
+ panic(FAULT);
+ }
+ ch = (char) i;
+ } catch (NumberFormatException nfe) {
+ panic(FAULT);
+ }
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (ch == ' ' || mInp.next != null) {
+ bappend(ch, flag);
+ } else {
+ bappend(ch);
+ }
+ st = -1;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+
+ return str;
+ }
+
+ /**
+ * Resoves a parameter entity.
+ *
+ * This method resolves a parameter entity references. It is also reports
+ * external entities to the application.
+ *
+ * @param flag The '-' instruct the method to do not set up surrounding
+ * spaces [#4.4.8].
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void pent(char flag) throws Exception {
+ char ch;
+ int idx = mBuffIdx + 1;
+ Input inp = null;
+ String str = null;
+ bappend('%');
+ if (mPh != PH_DTD) // the DTD internal subset
+ {
+ return; // Not Recognized [#4.4.1]
+ } // Read entity name
+ bname(false);
+ str = new String(mBuff, idx + 2, mBuffIdx - idx - 1);
+ if (getch() != ';') {
+ panic(FAULT);
+ }
+ inp = mPEnt.get(str);
+ // Restore the buffer offset
+ mBuffIdx = idx - 1;
+ if (inp != null) {
+ if (inp.chars == null) {
+ // External parameter entity
+ InputSource is = resolveEnt(str, inp.pubid, inp.sysid);
+ if (is != null) {
+ if (flag != '-') {
+ bappend(' '); // tail space
+ }
+ push(new Input(BUFFSIZE_READER));
+ // BUG: there is no leading space! [#4.4.8]
+ setinp(is);
+ mInp.pubid = inp.pubid;
+ mInp.sysid = inp.sysid;
+ } else {
+ // Unresolved external parameter entity
+ skippedEnt("%" + str);
+ }
+ } else {
+ // Internal parameter entity
+ if (flag == '-') {
+ // No surrounding spaces
+ inp.chIdx = 1;
+ } else {
+ // Insert surrounding spaces
+ bappend(' '); // tail space
+ inp.chIdx = 0;
+ }
+ push(inp);
+ }
+ } else {
+ // Unknown parameter entity
+ skippedEnt("%" + str);
+ }
+ }
+
+ /**
+ * Recognizes and handles a namespace declaration.
+ *
+ * This method identifies a type of namespace declaration if any and puts
+ * new mapping on top of prefix stack.
+ *
+ * @param name The attribute qualified name (<code>name.value</code> is a
+ * <code>String</code> object which represents the attribute prefix).
+ * @param value The attribute value.
+ * @return <code>true</code> if a namespace declaration is recognized.
+ */
+ private boolean isdecl(Pair name, String value) {
+ if (name.chars[0] == 0) {
+ if ("xmlns".equals(name.name) == true) {
+ // New default namespace declaration
+ mPref = pair(mPref);
+ mPref.list = mElm; // prefix owner element
+ mPref.value = value;
+ mPref.name = "";
+ mPref.chars = NONS;
+ mElm.num++; // namespace counter
+ return true;
+ }
+ } else {
+ if (name.eqpref(XMLNS) == true) {
+ // New prefix declaration
+ int len = name.name.length();
+ mPref = pair(mPref);
+ mPref.list = mElm; // prefix owner element
+ mPref.value = value;
+ mPref.name = name.name;
+ mPref.chars = new char[len + 1];
+ mPref.chars[0] = (char) (len + 1);
+ name.name.getChars(0, len, mPref.chars, 1);
+ mElm.num++; // namespace counter
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resolves a prefix.
+ *
+ * @return The namespace assigned to the prefix.
+ * @exception Exception When mapping for specified prefix is not found.
+ */
+ private String rslv(char[] qname)
+ throws Exception {
+ for (Pair pref = mPref; pref != null; pref = pref.next) {
+ if (pref.eqpref(qname) == true) {
+ return pref.value;
+ }
+ }
+ if (qname[0] == 1) { // QNames like ':local'
+ for (Pair pref = mPref; pref != null; pref = pref.next) {
+ if (pref.chars[0] == 0) {
+ return pref.value;
+ }
+ }
+ }
+ panic(FAULT);
+ return null;
+ }
+
+ /**
+ * Skips xml white space characters.
+ *
+ * This method skips white space characters (' ', '\t', '\n', '\r') and
+ * looks ahead not white space character.
+ *
+ * @return The first not white space look ahead character.
+ * @exception IOException
+ */
+ protected char wsskip()
+ throws IOException {
+ char ch;
+ while (true) {
+ // Read next character
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ if (ch < 0x80) {
+ if (nmttyp[ch] != 3) // [ \t\n\r]
+ {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ mChIdx--; // bkch();
+ return ch;
+ }
+
+ /**
+ * Reports document type.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity or <code>null</code>.
+ * @param sysid The system identifier of the entity or <code>null</code>.
+ */
+ protected abstract void docType(String name, String pubid, String sysid)
+ throws SAXException;
+
+ /**
+ * Reports a comment.
+ *
+ * @param text The comment text starting from first charcater.
+ * @param length The number of characters in comment.
+ */
+ protected abstract void comm(char[] text, int length);
+
+ /**
+ * Reports a processing instruction.
+ *
+ * @param target The processing instruction target name.
+ * @param body The processing instruction body text.
+ */
+ protected abstract void pi(String target, String body)
+ throws Exception;
+
+ /**
+ * Reports new namespace prefix. The Namespace prefix (
+ * <code>mPref.name</code>) being declared and the Namespace URI (
+ * <code>mPref.value</code>) the prefix is mapped to. An empty string is
+ * used for the default element namespace, which has no prefix.
+ */
+ protected abstract void newPrefix()
+ throws Exception;
+
+ /**
+ * Reports skipped entity name.
+ *
+ * @param name The entity name.
+ */
+ protected abstract void skippedEnt(String name)
+ throws Exception;
+
+ /**
+ * Returns an
+ * <code>InputSource</code> for specified entity or
+ * <code>null</code>.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity.
+ * @param sysid The system identifier of the entity.
+ */
+ protected abstract InputSource resolveEnt(
+ String name, String pubid, String sysid)
+ throws Exception;
+
+ /**
+ * Reports notation declaration.
+ *
+ * @param name The notation's name.
+ * @param pubid The notation's public identifier, or null if none was given.
+ * @param sysid The notation's system identifier, or null if none was given.
+ */
+ protected abstract void notDecl(String name, String pubid, String sysid)
+ throws Exception;
+
+ /**
+ * Reports unparsed entity name.
+ *
+ * @param name The unparsed entity's name.
+ * @param pubid The entity's public identifier, or null if none was given.
+ * @param sysid The entity's system identifier.
+ * @param notation The name of the associated notation.
+ */
+ protected abstract void unparsedEntDecl(
+ String name, String pubid, String sysid, String notation)
+ throws Exception;
+
+ /**
+ * Notifies the handler about fatal parsing error.
+ *
+ * @param msg The problem description message.
+ */
+ protected abstract void panic(String msg)
+ throws Exception;
+
+ /**
+ * Reads a qualified xml name.
+ *
+ * This is low level routine which leaves a qName in the buffer. The
+ * characters of a qualified name is an array of characters. The first
+ * (chars[0]) character is the index of the colon character which separates
+ * the prefix from the local name. If the index is zero, the name does not
+ * contain separator or the parser works in the namespace unaware mode. The
+ * length of qualified name is the length of the array minus one.
+ *
+ * @param ns The true value turns namespace conformance on.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private void bname(boolean ns)
+ throws Exception {
+ char ch;
+ char type;
+ mBuffIdx++; // allocate a char for colon offset
+ int bqname = mBuffIdx;
+ int bcolon = bqname;
+ int bchidx = bqname + 1;
+ int bstart = bchidx;
+ int cstart = mChIdx;
+ short st = (short) ((ns == true) ? 0 : 2);
+ while (true) {
+ // Read next character
+ if (mChIdx >= mChLen) {
+ bcopy(cstart, bstart);
+ getch();
+ mChIdx--; // bkch();
+ cstart = mChIdx;
+ bstart = bchidx;
+ }
+ ch = mChars[mChIdx++];
+ type = (char) 0; // [X]
+ if (ch < 0x80) {
+ type = (char) nmttyp[ch];
+ } else if (ch == EOS) {
+ panic(FAULT);
+ }
+ // Parse QName
+ switch (st) {
+ case 0: // read the first char of the prefix
+ case 2: // read the first char of the suffix
+ switch (type) {
+ case 0: // [aA_X]
+ bchidx++; // append char to the buffer
+ st++; // (st == 0)? 1: 3;
+ break;
+
+ case 1: // [:]
+ mChIdx--; // bkch();
+ st++; // (st == 0)? 1: 3;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 1: // read the prefix
+ case 3: // read the suffix
+ switch (type) {
+ case 0: // [aA_X]
+ case 2: // [.-d]
+ bchidx++; // append char to the buffer
+ break;
+
+ case 1: // [:]
+ bchidx++; // append char to the buffer
+ if (ns == true) {
+ if (bcolon != bqname) {
+ panic(FAULT); // it must be only one colon
+ }
+ bcolon = bchidx - 1;
+ if (st == 1) {
+ st = 2;
+ }
+ }
+ break;
+
+ default:
+ mChIdx--; // bkch();
+ bcopy(cstart, bstart);
+ mBuff[bqname] = (char) (bcolon - bqname);
+ return;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ }
+
+ /**
+ * Reads a nmtoken.
+ *
+ * This is low level routine which leaves a nmtoken in the buffer.
+ *
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void bntok() throws Exception {
+ char ch;
+ mBuffIdx = -1;
+ bappend((char) 0); // default offset to the colon char
+ while (true) {
+ ch = getch();
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case 'd':
+ case '.':
+ case ':':
+ case '-':
+ case '_':
+ case 'X':
+ bappend(ch);
+ break;
+
+ case 'Z':
+ panic(FAULT);
+
+ default:
+ bkch();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Recognizes a keyword.
+ *
+ * This is low level routine which recognizes one of keywords in the buffer.
+ * Keyword Id ID - i IDREF - r IDREFS - R ENTITY - n ENTITIES - N NMTOKEN -
+ * t NMTOKENS - T ELEMENT - e ATTLIST - a NOTATION - o CDATA - c REQUIRED -
+ * Q IMPLIED - I FIXED - F
+ *
+ * @return an id of a keyword or '?'.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private char bkeyword()
+ throws Exception {
+ String str = new String(mBuff, 1, mBuffIdx);
+ switch (str.length()) {
+ case 2: // ID
+ return ("ID".equals(str) == true) ? 'i' : '?';
+
+ case 5: // IDREF, CDATA, FIXED
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IDREF".equals(str) == true) ? 'r' : '?';
+ case 'C':
+ return ("CDATA".equals(str) == true) ? 'c' : '?';
+ case 'F':
+ return ("FIXED".equals(str) == true) ? 'F' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 6: // IDREFS, ENTITY
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IDREFS".equals(str) == true) ? 'R' : '?';
+ case 'E':
+ return ("ENTITY".equals(str) == true) ? 'n' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 7: // NMTOKEN, IMPLIED, ATTLIST, ELEMENT
+ switch (mBuff[1]) {
+ case 'I':
+ return ("IMPLIED".equals(str) == true) ? 'I' : '?';
+ case 'N':
+ return ("NMTOKEN".equals(str) == true) ? 't' : '?';
+ case 'A':
+ return ("ATTLIST".equals(str) == true) ? 'a' : '?';
+ case 'E':
+ return ("ELEMENT".equals(str) == true) ? 'e' : '?';
+ default:
+ break;
+ }
+ break;
+
+ case 8: // ENTITIES, NMTOKENS, NOTATION, REQUIRED
+ switch (mBuff[2]) {
+ case 'N':
+ return ("ENTITIES".equals(str) == true) ? 'N' : '?';
+ case 'M':
+ return ("NMTOKENS".equals(str) == true) ? 'T' : '?';
+ case 'O':
+ return ("NOTATION".equals(str) == true) ? 'o' : '?';
+ case 'E':
+ return ("REQUIRED".equals(str) == true) ? 'Q' : '?';
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return '?';
+ }
+
+ /**
+ * Reads a single or double quotted string in to the buffer.
+ *
+ * This method resolves entities inside a string unless the parser parses
+ * DTD.
+ *
+ * @param flag 'c' - CDATA, 'i' - non CDATA, ' ' - no normalization; '-' -
+ * not an attribute value; 'd' - in DTD context.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ @SuppressWarnings("fallthrough")
+ private void bqstr(char flag) throws Exception {
+ Input inp = mInp; // remember the original input
+ mBuffIdx = -1;
+ bappend((char) 0); // default offset to the colon char
+ char ch;
+ for (short st = 0; st >= 0;) {
+ ch = (mChIdx < mChLen) ? mChars[mChIdx++] : getch();
+ switch (st) {
+ case 0: // read a single or double quote
+ switch (ch) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ break;
+
+ case '\'':
+ st = 2; // read a single quoted string
+ break;
+
+ case '\"':
+ st = 3; // read a double quoted string
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ break;
+
+ case 2: // read a single quoted string
+ case 3: // read a double quoted string
+ switch (ch) {
+ case '\'':
+ if ((st == 2) && (mInp == inp)) {
+ st = -1;
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '\"':
+ if ((st == 3) && (mInp == inp)) {
+ st = -1;
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '&':
+ if (flag != 'd') {
+ ent(flag);
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '%':
+ if (flag == 'd') {
+ pent('-');
+ } else {
+ bappend(ch);
+ }
+ break;
+
+ case '<':
+ if ((flag == '-') || (flag == 'd')) {
+ bappend(ch);
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case EOS: // EOS before single/double quote
+ panic(FAULT);
+
+ case '\r': // EOL processing [#2.11 & #3.3.3]
+ if (flag != ' ' && mInp.next == null) {
+ if (getch() != '\n') {
+ bkch();
+ }
+ ch = '\n';
+ }
+ default:
+ bappend(ch, flag);
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ // There is maximum one space at the end of the string in
+ // i-mode (non CDATA normalization) and it has to be removed.
+ if ((flag == 'i') && (mBuff[mBuffIdx] == ' ')) {
+ mBuffIdx -= 1;
+ }
+ }
+
+ /**
+ * Reports characters and empties the parser's buffer. This method is called
+ * only if parser is going to return control to the main loop. This means
+ * that this method may use parser buffer to report white space without
+ * copeing characters to temporary buffer.
+ */
+ protected abstract void bflash()
+ throws Exception;
+
+ /**
+ * Reports white space characters and empties the parser's buffer. This
+ * method is called only if parser is going to return control to the main
+ * loop. This means that this method may use parser buffer to report white
+ * space without copeing characters to temporary buffer.
+ */
+ protected abstract void bflash_ws()
+ throws Exception;
+
+ /**
+ * Appends a character to parser's buffer with normalization.
+ *
+ * @param ch The character to append to the buffer.
+ * @param mode The normalization mode.
+ */
+ private void bappend(char ch, char mode) {
+ // This implements attribute value normalization as
+ // described in the XML specification [#3.3.3].
+ switch (mode) {
+ case 'i': // non CDATA normalization
+ switch (ch) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ if ((mBuffIdx > 0) && (mBuff[mBuffIdx] != ' ')) {
+ bappend(' ');
+ }
+ return;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'c': // CDATA normalization
+ switch (ch) {
+ case '\n':
+ case '\r':
+ case '\t':
+ ch = ' ';
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default: // no normalization
+ break;
+ }
+ mBuffIdx++;
+ if (mBuffIdx < mBuff.length) {
+ mBuff[mBuffIdx] = ch;
+ } else {
+ mBuffIdx--;
+ bappend(ch);
+ }
+ }
+
+ /**
+ * Appends a character to parser's buffer.
+ *
+ * @param ch The character to append to the buffer.
+ */
+ private void bappend(char ch) {
+ try {
+ mBuff[++mBuffIdx] = ch;
+ } catch (Exception exp) {
+ // Double the buffer size
+ char buff[] = new char[mBuff.length << 1];
+ System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
+ mBuff = buff;
+ mBuff[mBuffIdx] = ch;
+ }
+ }
+
+ /**
+ * Appends (mChIdx - cidx) characters from character buffer (mChars) to
+ * parser's buffer (mBuff).
+ *
+ * @param cidx The character buffer (mChars) start index.
+ * @param bidx The parser buffer (mBuff) start index.
+ */
+ private void bcopy(int cidx, int bidx) {
+ int length = mChIdx - cidx;
+ if ((bidx + length + 1) >= mBuff.length) {
+ // Expand the buffer
+ char buff[] = new char[mBuff.length + length];
+ System.arraycopy(mBuff, 0, buff, 0, mBuff.length);
+ mBuff = buff;
+ }
+ System.arraycopy(mChars, cidx, mBuff, bidx, length);
+ mBuffIdx += length;
+ }
+
+ /**
+ * Recognizes the built-in entities <i>lt</i>, <i>gt</i>, <i>amp</i>,
+ * <i>apos</i>, <i>quot</i>. The initial state is 0x100. Any state belowe
+ * 0x100 is a built-in entity replacement character.
+ *
+ * @param ch the next character of an entity name.
+ */
+ @SuppressWarnings("fallthrough")
+ private void eappend(char ch) {
+ switch (mESt) {
+ case 0x100: // "l" or "g" or "a" or "q"
+ switch (ch) {
+ case 'l':
+ mESt = 0x101;
+ break;
+ case 'g':
+ mESt = 0x102;
+ break;
+ case 'a':
+ mESt = 0x103;
+ break;
+ case 'q':
+ mESt = 0x107;
+ break;
+ default:
+ mESt = 0x200;
+ break;
+ }
+ break;
+
+ case 0x101: // "lt"
+ mESt = (ch == 't') ? '<' : (char) 0x200;
+ break;
+
+ case 0x102: // "gt"
+ mESt = (ch == 't') ? '>' : (char) 0x200;
+ break;
+
+ case 0x103: // "am" or "ap"
+ switch (ch) {
+ case 'm':
+ mESt = 0x104;
+ break;
+ case 'p':
+ mESt = 0x105;
+ break;
+ default:
+ mESt = 0x200;
+ break;
+ }
+ break;
+
+ case 0x104: // "amp"
+ mESt = (ch == 'p') ? '&' : (char) 0x200;
+ break;
+
+ case 0x105: // "apo"
+ mESt = (ch == 'o') ? (char) 0x106 : (char) 0x200;
+ break;
+
+ case 0x106: // "apos"
+ mESt = (ch == 's') ? '\'' : (char) 0x200;
+ break;
+
+ case 0x107: // "qu"
+ mESt = (ch == 'u') ? (char) 0x108 : (char) 0x200;
+ break;
+
+ case 0x108: // "quo"
+ mESt = (ch == 'o') ? (char) 0x109 : (char) 0x200;
+ break;
+
+ case 0x109: // "quot"
+ mESt = (ch == 't') ? '\"' : (char) 0x200;
+ break;
+
+ case '<': // "lt"
+ case '>': // "gt"
+ case '&': // "amp"
+ case '\'': // "apos"
+ case '\"': // "quot"
+ mESt = 0x200;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Sets up a new input source on the top of the input stack. Note, the first
+ * byte returned by the entity's byte stream has to be the first byte in the
+ * entity. However, the parser does not expect the byte order mask in both
+ * cases when encoding is provided by the input source.
+ *
+ * @param is A new input source to set up.
+ * @exception IOException If any IO errors occur.
+ * @exception Exception is parser specific exception form panic method.
+ */
+ protected void setinp(InputSource is)
+ throws Exception {
+ Reader reader = null;
+ mChIdx = 0;
+ mChLen = 0;
+ mChars = mInp.chars;
+ mInp.src = null;
+ if (mPh < PH_DOC_START) {
+ mIsSAlone = false; // default [#2.9]
+ }
+ mIsSAloneSet = false;
+ if (is.getCharacterStream() != null) {
+ // Ignore encoding in the xml text decl.
+ reader = is.getCharacterStream();
+ xml(reader);
+ } else if (is.getByteStream() != null) {
+ String expenc;
+ if (is.getEncoding() != null) {
+ // Ignore encoding in the xml text decl.
+ expenc = is.getEncoding().toUpperCase();
+ if (expenc.equals("UTF-16")) {
+ reader = bom(is.getByteStream(), 'U'); // UTF-16 [#4.3.3]
+ } else {
+ reader = enc(expenc, is.getByteStream());
+ }
+ xml(reader);
+ } else {
+ // Get encoding from BOM or the xml text decl.
+ reader = bom(is.getByteStream(), ' ');
+ if (reader == null) {
+ // Encoding is defined by the xml text decl.
+ reader = enc("UTF-8", is.getByteStream());
+ expenc = xml(reader);
+ if (expenc.startsWith("UTF-16")) {
+ panic(FAULT); // UTF-16 must have BOM [#4.3.3]
+ }
+ reader = enc(expenc, is.getByteStream());
+ } else {
+ // Encoding is defined by the BOM.
+ xml(reader);
+ }
+ }
+ } else {
+ // There is no support for public/system identifiers.
+ panic(FAULT);
+ }
+ mInp.src = reader;
+ mInp.pubid = is.getPublicId();
+ mInp.sysid = is.getSystemId();
+ }
+
+ /**
+ * Determines the entity encoding.
+ *
+ * This method gets encoding from Byte Order Mask [#4.3.3] if any. Note, the
+ * first byte returned by the entity's byte stream has to be the first byte
+ * in the entity. Also, there is no support for UCS-4.
+ *
+ * @param is A byte stream of the entity.
+ * @param hint An encoding hint, character U means UTF-16.
+ * @return a reader constructed from the BOM or UTF-8 by default.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private Reader bom(InputStream is, char hint)
+ throws Exception {
+ int val = is.read();
+ switch (val) {
+ case 0xef: // UTF-8
+ if (hint == 'U') // must be UTF-16
+ {
+ panic(FAULT);
+ }
+ if (is.read() != 0xbb) {
+ panic(FAULT);
+ }
+ if (is.read() != 0xbf) {
+ panic(FAULT);
+ }
+ return new ReaderUTF8(is);
+
+ case 0xfe: // UTF-16, big-endian
+ if (is.read() != 0xff) {
+ panic(FAULT);
+ }
+ return new ReaderUTF16(is, 'b');
+
+ case 0xff: // UTF-16, little-endian
+ if (is.read() != 0xfe) {
+ panic(FAULT);
+ }
+ return new ReaderUTF16(is, 'l');
+
+ case -1:
+ mChars[mChIdx++] = EOS;
+ return new ReaderUTF8(is);
+
+ default:
+ if (hint == 'U') // must be UTF-16
+ {
+ panic(FAULT);
+ }
+ // Read the rest of UTF-8 character
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ mChars[mChIdx++] = (char) (((val & 0x1f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xe0:
+ mChars[mChIdx++] = (char) (((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException();
+
+ default:
+ mChars[mChIdx++] = (char) val;
+ break;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Parses the xml text declaration.
+ *
+ * This method gets encoding from the xml text declaration [#4.3.1] if any.
+ * The method assumes the buffer (mChars) is big enough to accomodate whole
+ * xml text declaration.
+ *
+ * @param reader is entity reader.
+ * @return The xml text declaration encoding or default UTF-8 encoding.
+ * @exception Exception is parser specific exception form panic method.
+ * @exception IOException
+ */
+ private String xml(Reader reader)
+ throws Exception {
+ String str = null;
+ String enc = "UTF-8";
+ char ch;
+ int val;
+ short st;
+ // Read the xml text declaration into the buffer
+ if (mChIdx != 0) {
+ // The bom method have read ONE char into the buffer.
+ st = (short) ((mChars[0] == '<') ? 1 : -1);
+ } else {
+ st = 0;
+ }
+ while (st >= 0 && mChIdx < mChars.length) {
+ ch = ((val = reader.read()) >= 0) ? (char) val : EOS;
+ mChars[mChIdx++] = ch;
+ switch (st) {
+ case 0: // read '<' of xml declaration
+ switch (ch) {
+ case '<':
+ st = 1;
+ break;
+
+ case 0xfeff: // the byte order mask
+ ch = ((val = reader.read()) >= 0) ? (char) val : EOS;
+ mChars[mChIdx - 1] = ch;
+ st = (short) ((ch == '<') ? 1 : -1);
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 1: // read '?' of xml declaration [#4.3.1]
+ st = (short) ((ch == '?') ? 2 : -1);
+ break;
+
+ case 2: // read 'x' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'x') ? 3 : -1);
+ break;
+
+ case 3: // read 'm' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'm') ? 4 : -1);
+ break;
+
+ case 4: // read 'l' of xml declaration [#4.3.1]
+ st = (short) ((ch == 'l') ? 5 : -1);
+ break;
+
+ case 5: // read white space after 'xml'
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ st = 6;
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+ break;
+
+ case 6: // read content of xml declaration
+ switch (ch) {
+ case '?':
+ st = 7;
+ break;
+
+ case EOS:
+ st = -2;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 7: // read '>' after '?' of xml declaration
+ switch (ch) {
+ case '>':
+ case EOS:
+ st = -2;
+ break;
+
+ default:
+ st = 6;
+ break;
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ break;
+ }
+ }
+ mChLen = mChIdx;
+ mChIdx = 0;
+ // If there is no xml text declaration, the encoding is default.
+ if (st == -1) {
+ return enc;
+ }
+ mChIdx = 5; // the first white space after "<?xml"
+ // Parse the xml text declaration
+ for (st = 0; st >= 0;) {
+ ch = getch();
+ switch (st) {
+ case 0: // skip spaces after the xml declaration name
+ if (chtyp(ch) != ' ') {
+ bkch();
+ st = 1;
+ }
+ break;
+
+ case 1: // read xml declaration version
+ case 2: // read xml declaration encoding or standalone
+ case 3: // read xml declaration standalone
+ switch (chtyp(ch)) {
+ case 'a':
+ case 'A':
+ case '_':
+ bkch();
+ str = name(false).toLowerCase();
+ if ("version".equals(str) == true) {
+ if (st != 1) {
+ panic(FAULT);
+ }
+ if ("1.0".equals(eqstr('=')) != true) {
+ panic(FAULT);
+ }
+ mInp.xmlver = 0x0100;
+ st = 2;
+ } else if ("encoding".equals(str) == true) {
+ if (st != 2) {
+ panic(FAULT);
+ }
+ mInp.xmlenc = eqstr('=').toUpperCase();
+ enc = mInp.xmlenc;
+ st = 3;
+ } else if ("standalone".equals(str) == true) {
+ if ((st == 1) || (mPh >= PH_DOC_START)) // [#4.3.1]
+ {
+ panic(FAULT);
+ }
+ str = eqstr('=').toLowerCase();
+ // Check the 'standalone' value and use it [#5.1]
+ if (str.equals("yes") == true) {
+ mIsSAlone = true;
+ } else if (str.equals("no") == true) {
+ mIsSAlone = false;
+ } else {
+ panic(FAULT);
+ }
+ mIsSAloneSet = true;
+ st = 4;
+ } else {
+ panic(FAULT);
+ }
+ break;
+
+ case ' ':
+ break;
+
+ case '?':
+ if (st == 1) {
+ panic(FAULT);
+ }
+ bkch();
+ st = 4;
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ case 4: // end of xml declaration
+ switch (chtyp(ch)) {
+ case '?':
+ if (getch() != '>') {
+ panic(FAULT);
+ }
+ if (mPh <= PH_DOC_START) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ st = -1;
+ break;
+
+ case ' ':
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ }
+ return enc;
+ }
+
+ /**
+ * Sets up the document reader.
+ *
+ * @param name an encoding name.
+ * @param is the document byte input stream.
+ * @return a reader constructed from encoding name and input stream.
+ * @exception UnsupportedEncodingException
+ */
+ private Reader enc(String name, InputStream is)
+ throws UnsupportedEncodingException {
+ // DO NOT CLOSE current reader if any!
+ if (name.equals("UTF-8")) {
+ return new ReaderUTF8(is);
+ } else if (name.equals("UTF-16LE")) {
+ return new ReaderUTF16(is, 'l');
+ } else if (name.equals("UTF-16BE")) {
+ return new ReaderUTF16(is, 'b');
+ } else {
+ return new InputStreamReader(is, name);
+ }
+ }
+
+ /**
+ * Sets up current input on the top of the input stack.
+ *
+ * @param inp A new input to set up.
+ */
+ protected void push(Input inp) {
+ mInp.chLen = mChLen;
+ mInp.chIdx = mChIdx;
+ inp.next = mInp;
+ mInp = inp;
+ mChars = inp.chars;
+ mChLen = inp.chLen;
+ mChIdx = inp.chIdx;
+ }
+
+ /**
+ * Restores previous input on the top of the input stack.
+ */
+ protected void pop() {
+ if (mInp.src != null) {
+ try {
+ mInp.src.close();
+ } catch (IOException ioe) {
+ }
+ mInp.src = null;
+ }
+ mInp = mInp.next;
+ if (mInp != null) {
+ mChars = mInp.chars;
+ mChLen = mInp.chLen;
+ mChIdx = mInp.chIdx;
+ } else {
+ mChars = null;
+ mChLen = 0;
+ mChIdx = 0;
+ }
+ }
+
+ /**
+ * Maps a character to it's type.
+ *
+ * Possible character type values are:<br /> - ' ' for any kind of white
+ * space character;<br /> - 'a' for any lower case alphabetical character
+ * value;<br /> - 'A' for any upper case alphabetical character value;<br />
+ * - 'd' for any decimal digit character value;<br /> - 'z' for any
+ * character less then ' ' except '\t', '\n', '\r';<br /> - 'X' for any not
+ * ASCII character;<br /> - 'Z' for EOS character.<br /> An ASCII (7 bit)
+ * character which does not fall in any category listed above is mapped to
+ * it self.
+ *
+ * @param ch The character to map.
+ * @return The type of character.
+ */
+ protected char chtyp(char ch) {
+ if (ch < 0x80) {
+ return (char) asctyp[ch];
+ }
+ return (ch != EOS) ? 'X' : 'Z';
+ }
+
+ /**
+ * Retrives the next character in the document.
+ *
+ * @return The next character in the document.
+ */
+ protected char getch()
+ throws IOException {
+ if (mChIdx >= mChLen) {
+ if (mInp.src == null) {
+ pop(); // remove internal entity
+ return getch();
+ }
+ // Read new portion of the document characters
+ int Num = mInp.src.read(mChars, 0, mChars.length);
+ if (Num < 0) {
+ if (mInp != mDoc) {
+ pop(); // restore the previous input
+ return getch();
+ } else {
+ mChars[0] = EOS;
+ mChLen = 1;
+ }
+ } else {
+ mChLen = Num;
+ }
+ mChIdx = 0;
+ }
+ return mChars[mChIdx++];
+ }
+
+ /**
+ * Puts back the last read character.
+ *
+ * This method <strong>MUST NOT</strong> be called more then once after each
+ * call of {@link #getch getch} method.
+ */
+ protected void bkch()
+ throws Exception {
+ if (mChIdx <= 0) {
+ panic(FAULT);
+ }
+ mChIdx--;
+ }
+
+ /**
+ * Sets the current character.
+ *
+ * @param ch The character to set.
+ */
+ protected void setch(char ch) {
+ mChars[mChIdx] = ch;
+ }
+
+ /**
+ * Finds a pair in the pair chain by a qualified name.
+ *
+ * @param chain The first element of the chain of pairs.
+ * @param qname The qualified name.
+ * @return A pair with the specified qualified name or null.
+ */
+ protected Pair find(Pair chain, char[] qname) {
+ for (Pair pair = chain; pair != null; pair = pair.next) {
+ if (pair.eqname(qname) == true) {
+ return pair;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Provedes an instance of a pair.
+ *
+ * @param next The reference to a next pair.
+ * @return An instance of a pair.
+ */
+ protected Pair pair(Pair next) {
+ Pair pair;
+
+ if (mDltd != null) {
+ pair = mDltd;
+ mDltd = pair.next;
+ } else {
+ pair = new Pair();
+ }
+ pair.next = next;
+
+ return pair;
+ }
+
+ /**
+ * Deletes an instance of a pair.
+ *
+ * @param pair The pair to delete.
+ * @return A reference to the next pair in a chain.
+ */
+ protected Pair del(Pair pair) {
+ Pair next = pair.next;
+
+ pair.name = null;
+ pair.value = null;
+ pair.chars = null;
+ pair.list = null;
+ pair.next = mDltd;
+ mDltd = pair;
+
+ return next;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ParserSAX.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.ContentHandler;
+import jdk.internal.org.xml.sax.DTDHandler;
+import jdk.internal.org.xml.sax.EntityResolver;
+import jdk.internal.org.xml.sax.ErrorHandler;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.Locator;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.SAXParseException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * XML non-validating push parser.
+ *
+ * This non-validating parser conforms to <a href="http://www.w3.org/TR/REC-xml"
+ * >Extensible Markup Language (XML) 1.0</a> and <a
+ * href="http://www.w3.org/TR/REC-xml-names" >"Namespaces in XML"</a>
+ * specifications. The API supported by the parser are <a
+ * href="http://java.sun.com/aboutJava/communityprocess/final/jsr030/index.html">CLDC
+ * 1.0</a> and <a href="http://www.jcp.org/en/jsr/detail?id=280">JSR-280</a>, a
+ * JavaME subset of <a href="http://java.sun.com/xml/jaxp/index.html">JAXP</a>
+ * and <a href="http://www.saxproject.org/">SAX2</a>.
+ *
+ * @see org.xml.sax.XMLReader
+ */
+
+final class ParserSAX
+ extends Parser implements XMLReader, Locator
+{
+ public final static String FEATURE_NS =
+ "http://xml.org/sax/features/namespaces";
+ public final static String FEATURE_PREF =
+ "http://xml.org/sax/features/namespace-prefixes";
+ // SAX feature flags
+ private boolean mFNamespaces;
+ private boolean mFPrefixes;
+ // SAX handlers
+ private DefaultHandler mHand; // the default handler
+ private ContentHandler mHandCont; // the content handler
+ private DTDHandler mHandDtd; // the DTD handler
+ private ErrorHandler mHandErr; // the error handler
+ private EntityResolver mHandEnt; // the entity resolver
+
+ /**
+ * Constructor.
+ */
+ public ParserSAX() {
+ super();
+
+ // SAX feature defaut values
+ mFNamespaces = true;
+ mFPrefixes = false;
+
+ // Default handler which will be used in case the application
+ // do not set one of handlers.
+ mHand = new DefaultHandler();
+ mHandCont = mHand;
+ mHandDtd = mHand;
+ mHandErr = mHand;
+ mHandEnt = mHand;
+ }
+
+ /**
+ * Return the current content handler.
+ *
+ * @return The current content handler, or null if none has been registered.
+ * @see #setContentHandler
+ */
+ public ContentHandler getContentHandler() {
+ return (mHandCont != mHand) ? mHandCont : null;
+ }
+
+ /**
+ * Allow an application to register a content event handler.
+ *
+ * <p>If the application does not register a content handler, all content
+ * events reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The content handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getContentHandler
+ */
+ public void setContentHandler(ContentHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandCont = handler;
+ }
+
+ /**
+ * Return the current DTD handler.
+ *
+ * @return The current DTD handler, or null if none has been registered.
+ * @see #setDTDHandler
+ */
+ public DTDHandler getDTDHandler() {
+ return (mHandDtd != mHand) ? mHandDtd : null;
+ }
+
+ /**
+ * Allow an application to register a DTD event handler.
+ *
+ * <p>If the application does not register a DTD handler, all DTD events
+ * reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The DTD handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getDTDHandler
+ */
+ public void setDTDHandler(DTDHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandDtd = handler;
+ }
+
+ /**
+ * Return the current error handler.
+ *
+ * @return The current error handler, or null if none has been registered.
+ * @see #setErrorHandler
+ */
+ public ErrorHandler getErrorHandler() {
+ return (mHandErr != mHand) ? mHandErr : null;
+ }
+
+ /**
+ * Allow an application to register an error event handler.
+ *
+ * <p>If the application does not register an error handler, all error
+ * events reported by the SAX parser will be silently ignored; however,
+ * normal processing may not continue. It is highly recommended that all SAX
+ * applications implement an error handler to avoid unexpected bugs.</p>
+ *
+ * <p>Applications may register a new or different handler in the middle of
+ * a parse, and the SAX parser must begin using the new handler
+ * immediately.</p>
+ *
+ * @param handler The error handler.
+ * @exception java.lang.NullPointerException If the handler argument is
+ * null.
+ * @see #getErrorHandler
+ */
+ public void setErrorHandler(ErrorHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
+ mHandErr = handler;
+ }
+
+ /**
+ * Return the current entity resolver.
+ *
+ * @return The current entity resolver, or null if none has been registered.
+ * @see #setEntityResolver
+ */
+ public EntityResolver getEntityResolver() {
+ return (mHandEnt != mHand) ? mHandEnt : null;
+ }
+
+ /**
+ * Allow an application to register an entity resolver.
+ *
+ * <p>If the application does not register an entity resolver, the XMLReader
+ * will perform its own default resolution.</p>
+ *
+ * <p>Applications may register a new or different resolver in the middle of
+ * a parse, and the SAX parser must begin using the new resolver
+ * immediately.</p>
+ *
+ * @param resolver The entity resolver.
+ * @exception java.lang.NullPointerException If the resolver argument is
+ * null.
+ * @see #getEntityResolver
+ */
+ public void setEntityResolver(EntityResolver resolver) {
+ if (resolver == null) {
+ throw new NullPointerException();
+ }
+ mHandEnt = resolver;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ *
+ * <p>The return value is the public identifier of the document entity or of
+ * the external parsed entity in which the markup triggering the event
+ * appears.</p>
+ *
+ * @return A string containing the public identifier, or null if none is
+ * available.
+ *
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return (mInp != null) ? mInp.pubid : null;
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ *
+ * <p>The return value is the system identifier of the document entity or of
+ * the external parsed entity in which the markup triggering the event
+ * appears.</p>
+ *
+ * <p>If the system identifier is a URL, the parser must resolve it fully
+ * before passing it to the application.</p>
+ *
+ * @return A string containing the system identifier, or null if none is
+ * available.
+ *
+ * @see #getPublicId
+ */
+ public String getSystemId() {
+ return (mInp != null) ? mInp.sysid : null;
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ *
+ * @return Always returns -1 indicating the line number is not available.
+ *
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Return the column number where the current document event ends.
+ *
+ * @return Always returns -1 indicating the column number is not available.
+ *
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Parse an XML document from a system identifier (URI).
+ *
+ * <p>This method is a shortcut for the common case of reading a document
+ * from a system identifier. It is the exact equivalent of the
+ * following:</p>
+ *
+ * <pre>
+ * parse(new InputSource(systemId));
+ * </pre>
+ *
+ * <p>If the system identifier is a URL, it must be fully resolved by the
+ * application before it is passed to the parser.</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly wrapping
+ * another exception.
+ * @exception java.io.IOException An IO exception from the parser, possibly
+ * from a byte stream or character stream supplied by the application.
+ * @see #parse(org.xml.sax.InputSource)
+ */
+ public void parse(String systemId) throws IOException, SAXException {
+ parse(new InputSource(systemId));
+ }
+
+ /**
+ * Parse an XML document.
+ *
+ * <p>The application can use this method to instruct the XML reader to
+ * begin parsing an XML document from any valid input source (a character
+ * stream, a byte stream, or a URI).</p>
+ *
+ * <p>Applications may not invoke this method while a parse is in progress
+ * (they should create a new XMLReader instead for each nested XML
+ * document). Once a parse is complete, an application may reuse the same
+ * XMLReader object, possibly with a different input source.</p>
+ *
+ * <p>During the parse, the XMLReader will provide information about the XML
+ * document through the registered event handlers.</p>
+ *
+ * <p>This method is synchronous: it will not return until parsing has
+ * ended. If a client application wants to terminate parsing early, it
+ * should throw an exception.</p>
+ *
+ * @param is The input source for the top-level of the XML document.
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly wrapping
+ * another exception.
+ * @exception java.io.IOException An IO exception from the parser, possibly
+ * from a byte stream or character stream supplied by the application.
+ * @see org.xml.sax.InputSource
+ * @see #parse(java.lang.String)
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ */
+ public void parse(InputSource is) throws IOException, SAXException {
+ if (is == null) {
+ throw new IllegalArgumentException("");
+ }
+ // Set up the document
+ mInp = new Input(BUFFSIZE_READER);
+ mPh = PH_BEFORE_DOC; // before parsing
+ try {
+ setinp(is);
+ } catch (SAXException saxe) {
+ throw saxe;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (RuntimeException rte) {
+ throw rte;
+ } catch (Exception e) {
+ panic(e.toString());
+ }
+ parse();
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream} instance as
+ * XML using the specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param src InputStream containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the given InputStream or handler
+ * is null.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputStream src, DefaultHandler handler)
+ throws SAXException, IOException {
+ if ((src == null) || (handler == null)) {
+ throw new IllegalArgumentException("");
+ }
+ parse(new InputSource(src), handler);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource} as XML using the
+ * specified {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the InputSource or handler is
+ * null.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputSource is, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ if ((is == null) || (handler == null)) {
+ throw new IllegalArgumentException("");
+ }
+ // Set up the handler
+ mHandCont = handler;
+ mHandDtd = handler;
+ mHandErr = handler;
+ mHandEnt = handler;
+ // Set up the document
+ mInp = new Input(BUFFSIZE_READER);
+ mPh = PH_BEFORE_DOC; // before parsing
+ try {
+ setinp(is);
+ } catch (SAXException | IOException | RuntimeException saxe) {
+ throw saxe;
+ } catch (Exception e) {
+ panic(e.toString());
+ }
+ parse();
+ }
+
+ /**
+ * Parse the XML document content using specified handlers and an input
+ * source.
+ *
+ * @exception IOException If any IO errors occur.
+ * @exception SAXException If the underlying parser throws a SAXException
+ * while parsing.
+ */
+ @SuppressWarnings("fallthrough")
+ private void parse() throws SAXException, IOException {
+ init();
+ try {
+ mHandCont.setDocumentLocator(this);
+ mHandCont.startDocument();
+
+ if (mPh != PH_MISC_DTD) {
+ mPh = PH_MISC_DTD; // misc before DTD
+ }
+ int evt = EV_NULL;
+ // XML document prolog
+ do {
+ wsskip();
+ switch (evt = step()) {
+ case EV_ELM:
+ case EV_ELMS:
+ mPh = PH_DOCELM;
+ break;
+
+ case EV_COMM:
+ case EV_PI:
+ break;
+
+ case EV_DTD:
+ if (mPh >= PH_DTD_MISC) {
+ panic(FAULT);
+ }
+ mPh = PH_DTD_MISC; // misc after DTD
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh < PH_DOCELM); // misc before DTD
+ // XML document starting with document's element
+ do {
+ switch (evt) {
+ case EV_ELM:
+ case EV_ELMS:
+ // Report the element
+ if (mIsNSAware == true) {
+ mHandCont.startElement(
+ mElm.value,
+ mElm.name,
+ "",
+ mAttrs);
+ } else {
+ mHandCont.startElement(
+ "",
+ "",
+ mElm.name,
+ mAttrs);
+ }
+ if (evt == EV_ELMS) {
+ evt = step();
+ break;
+ }
+
+ case EV_ELME:
+ // Report the end of element
+ if (mIsNSAware == true) {
+ mHandCont.endElement(mElm.value, mElm.name, "");
+ } else {
+ mHandCont.endElement("", "", mElm.name);
+ }
+ // Restore the top of the prefix stack
+ while (mPref.list == mElm) {
+ mHandCont.endPrefixMapping(mPref.name);
+ mPref = del(mPref);
+ }
+ // Remove the top element tag
+ mElm = del(mElm);
+ if (mElm == null) {
+ mPh = PH_DOCELM_MISC;
+ } else {
+ evt = step();
+ }
+ break;
+
+ case EV_TEXT:
+ case EV_WSPC:
+ case EV_CDAT:
+ case EV_COMM:
+ case EV_PI:
+ case EV_ENT:
+ evt = step();
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh == PH_DOCELM);
+ // Misc after document's element
+ do {
+ if (wsskip() == EOS) {
+ break;
+ }
+
+ switch (step()) {
+ case EV_COMM:
+ case EV_PI:
+ break;
+
+ default:
+ panic(FAULT);
+ }
+ } while (mPh == PH_DOCELM_MISC);
+ mPh = PH_AFTER_DOC; // parsing is completed
+
+ } catch (SAXException saxe) {
+ throw saxe;
+ } catch (IOException ioe) {
+ throw ioe;
+ } catch (RuntimeException rte) {
+ throw rte;
+ } catch (Exception e) {
+ panic(e.toString());
+ } finally {
+ mHandCont.endDocument();
+ cleanup();
+ }
+ }
+
+ /**
+ * Reports document type.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity or <code>null</code>.
+ * @param sysid The system identifier of the entity or <code>null</code>.
+ */
+ protected void docType(String name, String pubid, String sysid) throws SAXException {
+ mHandDtd.notationDecl(name, pubid, sysid);
+ }
+
+ /**
+ * Reports a comment.
+ *
+ * @param text The comment text starting from first charcater.
+ * @param length The number of characters in comment.
+ */
+ protected void comm(char[] text, int length) {
+ }
+
+ /**
+ * Reports a processing instruction.
+ *
+ * @param target The processing instruction target name.
+ * @param body The processing instruction body text.
+ */
+ protected void pi(String target, String body) throws SAXException {
+ mHandCont.processingInstruction(target, body);
+ }
+
+ /**
+ * Reports new namespace prefix. The Namespace prefix (
+ * <code>mPref.name</code>) being declared and the Namespace URI (
+ * <code>mPref.value</code>) the prefix is mapped to. An empty string is
+ * used for the default element namespace, which has no prefix.
+ */
+ protected void newPrefix() throws SAXException {
+ mHandCont.startPrefixMapping(mPref.name, mPref.value);
+ }
+
+ /**
+ * Reports skipped entity name.
+ *
+ * @param name The entity name.
+ */
+ protected void skippedEnt(String name) throws SAXException {
+ mHandCont.skippedEntity(name);
+ }
+
+ /**
+ * Returns an
+ * <code>InputSource</code> for specified entity or
+ * <code>null</code>.
+ *
+ * @param name The name of the entity.
+ * @param pubid The public identifier of the entity.
+ * @param sysid The system identifier of the entity.
+ */
+ protected InputSource resolveEnt(String name, String pubid, String sysid)
+ throws SAXException, IOException
+ {
+ return mHandEnt.resolveEntity(pubid, sysid);
+ }
+
+ /**
+ * Reports notation declaration.
+ *
+ * @param name The notation's name.
+ * @param pubid The notation's public identifier, or null if none was given.
+ * @param sysid The notation's system identifier, or null if none was given.
+ */
+ protected void notDecl(String name, String pubid, String sysid)
+ throws SAXException
+ {
+ mHandDtd.notationDecl(name, pubid, sysid);
+ }
+
+ /**
+ * Reports unparsed entity name.
+ *
+ * @param name The unparsed entity's name.
+ * @param pubid The entity's public identifier, or null if none was given.
+ * @param sysid The entity's system identifier.
+ * @param notation The name of the associated notation.
+ */
+ protected void unparsedEntDecl(String name, String pubid, String sysid, String notation)
+ throws SAXException
+ {
+ mHandDtd.unparsedEntityDecl(name, pubid, sysid, notation);
+ }
+
+ /**
+ * Notifies the handler about fatal parsing error.
+ *
+ * @param msg The problem description message.
+ */
+ protected void panic(String msg) throws SAXException {
+ SAXParseException spe = new SAXParseException(msg, this);
+ mHandErr.fatalError(spe);
+ throw spe; // [#1.2] fatal error definition
+ }
+
+ /**
+ * Reports characters and empties the parser's buffer. This method is called
+ * only if parser is going to return control to the main loop. This means
+ * that this method may use parser buffer to report white space without
+ * copeing characters to temporary buffer.
+ */
+ protected void bflash() throws SAXException {
+ if (mBuffIdx >= 0) {
+ // Textual data has been read
+ mHandCont.characters(mBuff, 0, (mBuffIdx + 1));
+ mBuffIdx = -1;
+ }
+ }
+
+ /**
+ * Reports white space characters and empties the parser's buffer. This
+ * method is called only if parser is going to return control to the main
+ * loop. This means that this method may use parser buffer to report white
+ * space without copeing characters to temporary buffer.
+ */
+ protected void bflash_ws() throws SAXException {
+ if (mBuffIdx >= 0) {
+ // BUG: With additional info from DTD and xml:space attr [#2.10]
+ // the following call can be supported:
+ // mHandCont.ignorableWhitespace(mBuff, 0, (mBuffIdx + 1));
+
+ // Textual data has been read
+ mHandCont.characters(mBuff, 0, (mBuffIdx + 1));
+ mBuffIdx = -1;
+ }
+ }
+
+ public boolean getFeature(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setFeature(String name, boolean value) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Object getProperty(String name) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setProperty(String name, Object value) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ReaderUTF16.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * UTF-16 encoded stream reader.
+ */
+public class ReaderUTF16 extends Reader {
+
+ private InputStream is;
+ private char bo;
+
+ /**
+ * Constructor.
+ *
+ * Byte order argument can be: 'l' for little-endian or 'b' for big-endian.
+ *
+ * @param is A byte input stream.
+ * @param bo A byte order in the input stream.
+ */
+ public ReaderUTF16(InputStream is, char bo) {
+ switch (bo) {
+ case 'l':
+ break;
+
+ case 'b':
+ break;
+
+ default:
+ throw new IllegalArgumentException("");
+ }
+ this.bo = bo;
+ this.is = is;
+ }
+
+ /**
+ * Reads characters into a portion of an array.
+ *
+ * @param cbuf Destination buffer.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ * @exception IOException If any IO errors occur.
+ */
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int num = 0;
+ int val;
+ if (bo == 'b') {
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ cbuf[off++] = (char) ((val << 8) | (is.read() & 0xff));
+ num++;
+ }
+ } else {
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ cbuf[off++] = (char) ((is.read() << 8) | (val & 0xff));
+ num++;
+ }
+ }
+ return num;
+ }
+
+ /**
+ * Reads a single character.
+ *
+ * @return The character read, as an integer in the range 0 to 65535
+ * (0x0000-0xffff), or -1 if the end of the stream has been reached.
+ * @exception IOException If any IO errors occur.
+ */
+ public int read() throws IOException {
+ int val;
+ if ((val = is.read()) < 0) {
+ return -1;
+ }
+ if (bo == 'b') {
+ val = (char) ((val << 8) | (is.read() & 0xff));
+ } else {
+ val = (char) ((is.read() << 8) | (val & 0xff));
+ }
+ return val;
+ }
+
+ /**
+ * Closes the stream.
+ *
+ * @exception IOException If any IO errors occur.
+ */
+ public void close() throws IOException {
+ is.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/ReaderUTF8.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * UTF-8 transformed UCS-2 character stream reader.
+ *
+ * This reader converts UTF-8 transformed UCS-2 characters to Java characters.
+ * The UCS-2 subset of UTF-8 transformation is described in RFC-2279 #2
+ * "UTF-8 definition":
+ * 0000 0000-0000 007F 0xxxxxxx
+ * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
+ *
+ * This reader will return incorrect last character on broken UTF-8 stream.
+ */
+public class ReaderUTF8 extends Reader {
+
+ private InputStream is;
+
+ /**
+ * Constructor.
+ *
+ * @param is A byte input stream.
+ */
+ public ReaderUTF8(InputStream is) {
+ this.is = is;
+ }
+
+ /**
+ * Reads characters into a portion of an array.
+ *
+ * @param cbuf Destination buffer.
+ * @param off Offset at which to start storing characters.
+ * @param len Maximum number of characters to read.
+ * @exception IOException If any IO errors occur.
+ * @exception UnsupportedEncodingException If UCS-4 character occur in the stream.
+ */
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int num = 0;
+ int val;
+ while (num < len) {
+ if ((val = is.read()) < 0) {
+ return (num != 0) ? num : -1;
+ }
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ cbuf[off++] = (char) (((val & 0x1f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xe0:
+ cbuf[off++] = (char) (((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f));
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException("UTF-32 (or UCS-4) encoding not supported.");
+
+ default:
+ cbuf[off++] = (char) val;
+ break;
+ }
+ num++;
+ }
+ return num;
+ }
+
+ /**
+ * Reads a single character.
+ *
+ * @return The character read, as an integer in the range 0 to 65535
+ * (0x00-0xffff), or -1 if the end of the stream has been reached.
+ * @exception IOException If any IO errors occur.
+ * @exception UnsupportedEncodingException If UCS-4 character occur in the stream.
+ */
+ public int read() throws IOException {
+ int val;
+ if ((val = is.read()) < 0) {
+ return -1;
+ }
+ switch (val & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ val = ((val & 0x1f) << 6) | (is.read() & 0x3f);
+ break;
+
+ case 0xe0:
+ val = ((val & 0x0f) << 12)
+ | ((is.read() & 0x3f) << 6) | (is.read() & 0x3f);
+ break;
+
+ case 0xf0: // UCS-4 character
+ throw new UnsupportedEncodingException();
+
+ default:
+ break;
+ }
+ return val;
+ }
+
+ /**
+ * Closes the stream.
+ *
+ * @exception IOException If any IO errors occur.
+ */
+ public void close() throws IOException {
+ is.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/SAXParserImpl.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import jdk.internal.org.xml.sax.InputSource;
+import jdk.internal.org.xml.sax.SAXException;
+import jdk.internal.org.xml.sax.XMLReader;
+import jdk.internal.org.xml.sax.helpers.DefaultHandler;
+import jdk.internal.util.xml.SAXParser;
+
+public class SAXParserImpl extends SAXParser {
+
+ private ParserSAX parser;
+
+ public SAXParserImpl() {
+ super();
+ parser = new ParserSAX();
+ }
+
+ /**
+ * Returns the {@link org.xml.sax.XMLReader} that is encapsulated by the
+ * implementation of this class.
+ *
+ * @return The XMLReader that is encapsulated by the
+ * implementation of this class.
+ *
+ * @throws SAXException If any SAX errors occur during processing.
+ */
+ public XMLReader getXMLReader()
+ throws SAXException {
+ return parser;
+ }
+
+ /**
+ * Indicates whether or not this parser is configured to
+ * understand namespaces.
+ *
+ * @return true if this parser is configured to
+ * understand namespaces; false otherwise.
+ */
+ public boolean isNamespaceAware() {
+ return parser.mIsNSAware;
+ }
+
+ /**
+ * Indicates whether or not this parser is configured to validate
+ * XML documents.
+ *
+ * @return true if this parser is configured to validate XML
+ * documents; false otherwise.
+ */
+ public boolean isValidating() {
+ return false;
+ }
+
+ /**
+ * Parse the content of the given {@link java.io.InputStream}
+ * instance as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param src InputStream containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the given InputStream or handler is null.
+ * @exception SAXException If the underlying parser throws a
+ * SAXException while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputStream src, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ parser.parse(src, handler);
+ }
+
+ /**
+ * Parse the content given {@link org.xml.sax.InputSource}
+ * as XML using the specified
+ * {@link org.xml.sax.helpers.DefaultHandler}.
+ *
+ * @param is The InputSource containing the content to be parsed.
+ * @param handler The SAX DefaultHandler to use.
+ * @exception IOException If any IO errors occur.
+ * @exception IllegalArgumentException If the InputSource or handler is null.
+ * @exception SAXException If the underlying parser throws a
+ * SAXException while parsing.
+ * @see org.xml.sax.helpers.DefaultHandler
+ */
+ public void parse(InputSource is, DefaultHandler handler)
+ throws SAXException, IOException
+ {
+ parser.parse(is, handler);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/XMLStreamWriterImpl.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import jdk.internal.util.xml.XMLStreamException;
+import jdk.internal.util.xml.XMLStreamWriter;
+
+/**
+ * Implementation of a reduced version of XMLStreamWriter
+ *
+ * @author Joe Wang
+ */
+public class XMLStreamWriterImpl implements XMLStreamWriter {
+ //Document state
+
+ static final int STATE_XML_DECL = 1;
+ static final int STATE_PROLOG = 2;
+ static final int STATE_DTD_DECL = 3;
+ static final int STATE_ELEMENT = 4;
+ //Element state
+ static final int ELEMENT_STARTTAG_OPEN = 10;
+ static final int ELEMENT_STARTTAG_CLOSE = 11;
+ static final int ELEMENT_ENDTAG_OPEN = 12;
+ static final int ELEMENT_ENDTAG_CLOSE = 13;
+ public static final char CLOSE_START_TAG = '>';
+ public static final char OPEN_START_TAG = '<';
+ public static final String OPEN_END_TAG = "</";
+ public static final char CLOSE_END_TAG = '>';
+ public static final String START_CDATA = "<![CDATA[";
+ public static final String END_CDATA = "]]>";
+ public static final String CLOSE_EMPTY_ELEMENT = "/>";
+ public static final String ENCODING_PREFIX = "&#x";
+ public static final char SPACE = ' ';
+ public static final char AMPERSAND = '&';
+ public static final char DOUBLEQUOT = '"';
+ public static final char SEMICOLON = ';';
+ //current state
+ private int _state = 0;
+ private Element _currentEle;
+ private XMLWriter _writer;
+ private String _encoding;
+ /**
+ * This flag can be used to turn escaping off for content. It does
+ * not apply to attribute content.
+ */
+ boolean _escapeCharacters = true;
+ //pretty print by default
+ private boolean _doIndent = true;
+ //The system line separator for writing out line breaks.
+ private char[] _lineSep =
+ System.getProperty("line.separator").toCharArray();
+
+ public XMLStreamWriterImpl(OutputStream os) throws XMLStreamException {
+ this(os, XMLStreamWriter.DEFAULT_ENCODING);
+ }
+
+ public XMLStreamWriterImpl(OutputStream os, String encoding)
+ throws XMLStreamException
+ {
+ Charset cs = null;
+ if (encoding == null) {
+ _encoding = XMLStreamWriter.DEFAULT_ENCODING;
+ } else {
+ try {
+ cs = getCharset(encoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new XMLStreamException(e);
+ }
+
+ this._encoding = encoding;
+ }
+
+ _writer = new XMLWriter(os, encoding, cs);
+ }
+
+ /**
+ * Write the XML Declaration. Defaults the XML version to 1.0, and the
+ * encoding to utf-8.
+ *
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument() throws XMLStreamException {
+ writeStartDocument(_encoding, XMLStreamWriter.DEFAULT_XML_VERSION);
+ }
+
+ /**
+ * Write the XML Declaration. Defaults the encoding to utf-8
+ *
+ * @param version version of the xml document
+ * @throws XMLStreamException
+ */
+ public void writeStartDocument(String version) throws XMLStreamException {
+ writeStartDocument(_encoding, version, null);
+ }
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does not set
+ * the actual encoding of the underlying output. That must be set when the
+ * instance of the XMLStreamWriter is created
+ *
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @throws XMLStreamException If given encoding does not match encoding of the
+ * underlying stream
+ */
+ public void writeStartDocument(String encoding, String version) throws XMLStreamException {
+ writeStartDocument(encoding, version, null);
+ }
+
+ /**
+ * Write the XML Declaration. Note that the encoding parameter does not set
+ * the actual encoding of the underlying output. That must be set when the
+ * instance of the XMLStreamWriter is created
+ *
+ * @param encoding encoding of the xml declaration
+ * @param version version of the xml document
+ * @param standalone indicate if the xml document is standalone
+ * @throws XMLStreamException If given encoding does not match encoding of the
+ * underlying stream
+ */
+ public void writeStartDocument(String encoding, String version, String standalone)
+ throws XMLStreamException
+ {
+ if (_state > 0) {
+ throw new XMLStreamException("XML declaration must be as the first line in the XML document.");
+ }
+ _state = STATE_XML_DECL;
+ String enc = encoding;
+ if (enc == null) {
+ enc = _encoding;
+ } else {
+ //check if the encoding is supported
+ try {
+ getCharset(encoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ if (version == null) {
+ version = XMLStreamWriter.DEFAULT_XML_VERSION;
+ }
+
+ _writer.write("<?xml version=\"");
+ _writer.write(version);
+ _writer.write(DOUBLEQUOT);
+
+ if (enc != null) {
+ _writer.write(" encoding=\"");
+ _writer.write(enc);
+ _writer.write(DOUBLEQUOT);
+ }
+
+ if (standalone != null) {
+ _writer.write(" standalone=\"");
+ _writer.write(standalone);
+ _writer.write(DOUBLEQUOT);
+ }
+ _writer.write("?>");
+ writeLineSeparator();
+ }
+
+ /**
+ * Write a DTD section. This string represents the entire doctypedecl production
+ * from the XML 1.0 specification.
+ *
+ * @param dtd the DTD to be written
+ * @throws XMLStreamException
+ */
+ public void writeDTD(String dtd) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+ _writer.write(dtd);
+ writeLineSeparator();
+ }
+
+ /**
+ * Writes a start tag to the output.
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeStartElement(String localName) throws XMLStreamException {
+ if (localName == null || localName.length() == 0) {
+ throw new XMLStreamException("Local Name cannot be null or empty");
+ }
+
+ _state = STATE_ELEMENT;
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _currentEle = new Element(_currentEle, localName, false);
+ openStartTag();
+
+ _writer.write(localName);
+ }
+
+ /**
+ * Writes an empty element tag to the output
+ * @param localName local name of the tag, may not be null
+ * @throws XMLStreamException
+ */
+ public void writeEmptyElement(String localName) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _currentEle = new Element(_currentEle, localName, true);
+
+ openStartTag();
+ _writer.write(localName);
+ }
+
+ /**
+ * Writes an attribute to the output stream without a prefix.
+ * @param localName the local name of the attribute
+ * @param value the value of the attribute
+ * @throws IllegalStateException if the current state does not allow Attribute writing
+ * @throws XMLStreamException
+ */
+ public void writeAttribute(String localName, String value) throws XMLStreamException {
+ if (_currentEle.getState() != ELEMENT_STARTTAG_OPEN) {
+ throw new XMLStreamException(
+ "Attribute not associated with any element");
+ }
+
+ _writer.write(SPACE);
+ _writer.write(localName);
+ _writer.write("=\"");
+ writeXMLContent(
+ value,
+ true, // true = escapeChars
+ true); // true = escapeDoubleQuotes
+ _writer.write(DOUBLEQUOT);
+ }
+
+ public void writeEndDocument() throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ /**
+ * close unclosed elements if any
+ */
+ while (_currentEle != null) {
+
+ if (!_currentEle.isEmpty()) {
+ _writer.write(OPEN_END_TAG);
+ _writer.write(_currentEle.getLocalName());
+ _writer.write(CLOSE_END_TAG);
+ }
+
+ _currentEle = _currentEle.getParent();
+ }
+ }
+
+ public void writeEndElement() throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ if (_currentEle == null) {
+ throw new XMLStreamException("No element was found to write");
+ }
+
+ if (_currentEle.isEmpty()) {
+ return;
+ }
+
+ _writer.write(OPEN_END_TAG);
+ _writer.write(_currentEle.getLocalName());
+ _writer.write(CLOSE_END_TAG);
+ writeLineSeparator();
+
+ _currentEle = _currentEle.getParent();
+ }
+
+ public void writeCData(String cdata) throws XMLStreamException {
+ if (cdata == null) {
+ throw new XMLStreamException("cdata cannot be null");
+ }
+
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ _writer.write(START_CDATA);
+ _writer.write(cdata);
+ _writer.write(END_CDATA);
+ }
+
+ public void writeCharacters(String data) throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ writeXMLContent(data);
+ }
+
+ public void writeCharacters(char[] data, int start, int len)
+ throws XMLStreamException {
+ if (_currentEle != null && _currentEle.getState() == ELEMENT_STARTTAG_OPEN) {
+ closeStartTag();
+ }
+
+ writeXMLContent(data, start, len, _escapeCharacters);
+ }
+
+ /**
+ * Close this XMLStreamWriter by closing underlying writer.
+ */
+ public void close() throws XMLStreamException {
+ if (_writer != null) {
+ _writer.close();
+ }
+ _writer = null;
+ _currentEle = null;
+ _state = 0;
+ }
+
+ /**
+ * Flush this XMLStreamWriter by flushing underlying writer.
+ */
+ public void flush() throws XMLStreamException {
+ if (_writer != null) {
+ _writer.flush();
+ }
+ }
+
+ /**
+ * Set the flag to indicate if the writer should add line separator
+ * @param doIndent
+ */
+ public void setDoIndent(boolean doIndent) {
+ _doIndent = doIndent;
+ }
+
+ /**
+ * Writes XML content to underlying writer. Escapes characters unless
+ * escaping character feature is turned off.
+ */
+ private void writeXMLContent(char[] content, int start, int length, boolean escapeChars)
+ throws XMLStreamException
+ {
+ if (!escapeChars) {
+ _writer.write(content, start, length);
+ return;
+ }
+
+ // Index of the next char to be written
+ int startWritePos = start;
+
+ final int end = start + length;
+
+ for (int index = start; index < end; index++) {
+ char ch = content[index];
+
+ if (!_writer.canEncode(ch)) {
+ _writer.write(content, startWritePos, index - startWritePos);
+
+ // Escape this char as underlying encoder cannot handle it
+ _writer.write(ENCODING_PREFIX);
+ _writer.write(Integer.toHexString(ch));
+ _writer.write(SEMICOLON);
+ startWritePos = index + 1;
+ continue;
+ }
+
+ switch (ch) {
+ case OPEN_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("<");
+ startWritePos = index + 1;
+
+ break;
+
+ case AMPERSAND:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("&");
+ startWritePos = index + 1;
+
+ break;
+
+ case CLOSE_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write(">");
+ startWritePos = index + 1;
+
+ break;
+ }
+ }
+
+ // Write any pending data
+ _writer.write(content, startWritePos, end - startWritePos);
+ }
+
+ private void writeXMLContent(String content) throws XMLStreamException {
+ if ((content != null) && (content.length() > 0)) {
+ writeXMLContent(content,
+ _escapeCharacters, // boolean = escapeChars
+ false); // false = escapeDoubleQuotes
+ }
+ }
+
+ /**
+ * Writes XML content to underlying writer. Escapes characters unless
+ * escaping character feature is turned off.
+ */
+ private void writeXMLContent(
+ String content,
+ boolean escapeChars,
+ boolean escapeDoubleQuotes)
+ throws XMLStreamException
+ {
+
+ if (!escapeChars) {
+ _writer.write(content);
+
+ return;
+ }
+
+ // Index of the next char to be written
+ int startWritePos = 0;
+
+ final int end = content.length();
+
+ for (int index = 0; index < end; index++) {
+ char ch = content.charAt(index);
+
+ if (!_writer.canEncode(ch)) {
+ _writer.write(content, startWritePos, index - startWritePos);
+
+ // Escape this char as underlying encoder cannot handle it
+ _writer.write(ENCODING_PREFIX);
+ _writer.write(Integer.toHexString(ch));
+ _writer.write(SEMICOLON);
+ startWritePos = index + 1;
+ continue;
+ }
+
+ switch (ch) {
+ case OPEN_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("<");
+ startWritePos = index + 1;
+
+ break;
+
+ case AMPERSAND:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write("&");
+ startWritePos = index + 1;
+
+ break;
+
+ case CLOSE_START_TAG:
+ _writer.write(content, startWritePos, index - startWritePos);
+ _writer.write(">");
+ startWritePos = index + 1;
+
+ break;
+
+ case DOUBLEQUOT:
+ _writer.write(content, startWritePos, index - startWritePos);
+ if (escapeDoubleQuotes) {
+ _writer.write(""");
+ } else {
+ _writer.write(DOUBLEQUOT);
+ }
+ startWritePos = index + 1;
+
+ break;
+ }
+ }
+
+ // Write any pending data
+ _writer.write(content, startWritePos, end - startWritePos);
+ }
+
+ /**
+ * marks open of start tag and writes the same into the writer.
+ */
+ private void openStartTag() throws XMLStreamException {
+ _currentEle.setState(ELEMENT_STARTTAG_OPEN);
+ _writer.write(OPEN_START_TAG);
+ }
+
+ /**
+ * marks close of start tag and writes the same into the writer.
+ */
+ private void closeStartTag() throws XMLStreamException {
+ if (_currentEle.isEmpty()) {
+ _writer.write(CLOSE_EMPTY_ELEMENT);
+ } else {
+ _writer.write(CLOSE_START_TAG);
+
+ }
+
+ if (_currentEle.getParent() == null) {
+ writeLineSeparator();
+ }
+
+ _currentEle.setState(ELEMENT_STARTTAG_CLOSE);
+
+ }
+
+ /**
+ * Write a line separator
+ * @throws XMLStreamException
+ */
+ private void writeLineSeparator() throws XMLStreamException {
+ if (_doIndent) {
+ _writer.write(_lineSep, 0, _lineSep.length);
+ }
+ }
+
+ /**
+ * Returns a charset object for the specified encoding
+ * @param encoding
+ * @return a charset object
+ * @throws UnsupportedEncodingException if the encoding is not supported
+ */
+ private Charset getCharset(String encoding) throws UnsupportedEncodingException {
+ if (encoding.equalsIgnoreCase("UTF-32")) {
+ throw new UnsupportedEncodingException("The basic XMLWriter does "
+ + "not support " + encoding);
+ }
+
+ Charset cs;
+ try {
+ cs = Charset.forName(encoding);
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException ex) {
+ throw new UnsupportedEncodingException(encoding);
+ }
+ return cs;
+ }
+
+ /*
+ * Start of Internal classes.
+ *
+ */
+ protected class Element {
+
+ /**
+ * the parent element
+ */
+ protected Element _parent;
+ /**
+ * The size of the stack.
+ */
+ protected short _Depth;
+ /**
+ * indicate if an element is an empty one
+ */
+ boolean _isEmptyElement = false;
+ String _localpart;
+ int _state;
+
+ /**
+ * Default constructor.
+ */
+ public Element() {
+ }
+
+ /**
+ * @param parent the parent of the element
+ * @param localpart name of the element
+ * @param isEmpty indicate if the element is an empty one
+ */
+ public Element(Element parent, String localpart, boolean isEmpty) {
+ _parent = parent;
+ _localpart = localpart;
+ _isEmptyElement = isEmpty;
+ }
+
+ public Element getParent() {
+ return _parent;
+ }
+
+ public String getLocalName() {
+ return _localpart;
+ }
+
+ /**
+ * get the state of the element
+ */
+ public int getState() {
+ return _state;
+ }
+
+ /**
+ * Set the state of the element
+ *
+ * @param state the state of the element
+ */
+ public void setState(int state) {
+ _state = state;
+ }
+
+ public boolean isEmpty() {
+ return _isEmptyElement;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/jdk/internal/util/xml/impl/XMLWriter.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+
+package jdk.internal.util.xml.impl;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import jdk.internal.util.xml.XMLStreamException;
+
+/**
+ *
+ * @author huizwang
+ */
+public class XMLWriter {
+
+ private Writer _writer;
+ /**
+ * In some cases, this charset encoder is used to determine if a char is
+ * encodable by underlying writer. For example, an 8-bit char from the
+ * extended ASCII set is not encodable by 7-bit ASCII encoder. Unencodable
+ * chars are escaped using XML numeric entities.
+ */
+ private CharsetEncoder _encoder = null;
+
+ public XMLWriter(OutputStream os, String encoding, Charset cs) throws XMLStreamException {
+ _encoder = cs.newEncoder();
+ try {
+ _writer = getWriter(os, encoding, cs);
+ } catch (UnsupportedEncodingException ex) {
+ throw new XMLStreamException(ex);
+ }
+
+ }
+
+ public boolean canEncode(char ch) {
+ if (_encoder == null) {
+ return false;
+ }
+ return (_encoder.canEncode(ch));
+ }
+
+ public void write(String s)
+ throws XMLStreamException {
+ try {
+ _writer.write(s.toCharArray());
+// _writer.write(s.getBytes(Charset.forName(_encoding)));
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ public void write(String str, int off, int len)
+ throws XMLStreamException {
+ try {
+ _writer.write(str, off, len);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+
+ }
+
+ public void write(char[] cbuf, int off, int len)
+ throws XMLStreamException {
+ try {
+ _writer.write(cbuf, off, len);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+
+ }
+
+ void write(int b)
+ throws XMLStreamException {
+ try {
+ _writer.write(b);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ void flush() throws XMLStreamException {
+ try {
+ _writer.flush();
+ } catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ void close() throws XMLStreamException {
+ try {
+ _writer.close();
+ } catch (IOException ex) {
+ throw new XMLStreamException(ex);
+ }
+ }
+
+ private void nl() throws XMLStreamException {
+ String lineEnd = System.getProperty("line.separator");
+ try {
+ _writer.write(lineEnd);
+ } catch (IOException e) {
+ throw new XMLStreamException("I/O error", e);
+ }
+ }
+
+ /**
+ * Returns a writer for the specified encoding based on an output stream.
+ *
+ * @param output The output stream
+ * @param encoding The encoding
+ * @return A suitable writer
+ * @throws UnsupportedEncodingException There is no convertor to support
+ * this encoding
+ */
+ private Writer getWriter(OutputStream output, String encoding, Charset cs)
+ throws XMLStreamException, UnsupportedEncodingException
+ {
+ if (cs != null) {
+ return (new OutputStreamWriter(new BufferedOutputStream(output), cs));
+ }
+
+ return new OutputStreamWriter(new BufferedOutputStream(output), encoding);
+ }
+}
--- a/jdk/src/share/classes/sun/net/www/http/KeepAliveStream.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/share/classes/sun/net/www/http/KeepAliveStream.java Wed Dec 26 10:08:36 2012 -0500
@@ -83,11 +83,8 @@
if (expected > count) {
long nskip = expected - count;
if (nskip <= available()) {
- long n = 0;
- while (n < nskip) {
- nskip = nskip - n;
- n = skip(nskip);
- }
+ do {} while ((nskip = (expected - count)) > 0L
+ && skip(Math.min(nskip, available())) > 0L);
} else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) {
//put this KeepAliveStream on the queue so that the data remaining
//on the socket can be cleanup asyncronously.
--- a/jdk/src/solaris/classes/sun/print/UnixPrintJob.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/src/solaris/classes/sun/print/UnixPrintJob.java Wed Dec 26 10:08:36 2012 -0500
@@ -939,7 +939,7 @@
* is not removed for some reason, request that it is
* removed when the VM exits.
*/
- spoolFile = Files.createTempFile("javaprint", ".ps").toFile();
+ spoolFile = Files.createTempFile("javaprint", "").toFile();
spoolFile.deleteOnExit();
}
result = new FileOutputStream(spoolFile);
--- a/jdk/test/ProblemList.txt Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/ProblemList.txt Wed Dec 26 10:08:36 2012 -0500
@@ -141,9 +141,6 @@
# jdk_management
-# 7158614
-sun/management/jmxremote/startstop/JMXStartStopTest.sh linux-all
-
############################################################################
# jdk_jmx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/CCAdminReconnectTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, 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 7009998
+ * @summary Tests the correct processing of concurrent ClientComunicatorAdmin reconnect requests.
+ * @author Jaroslav Bachorik
+ * @run clean CCAdminReconnectTest
+ * @run build CCAdminReconnectTest
+ * @run main CCAdminReconnectTest
+ */
+
+import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
+import java.io.*;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class CCAdminReconnectTest {
+ final private static int THREAD_COUNT = 3;
+
+ public static void main(String ... args) throws Exception {
+ final ExecutorService e = Executors.newFixedThreadPool(THREAD_COUNT);
+ final AtomicBoolean beingReconnected = new AtomicBoolean();
+ final Collection<Exception> thrownExceptions = new LinkedList<>();
+
+ System.out.println(": Testing concurrent restart of ClientCommunicatorAdmin");
+
+ final ClientCommunicatorAdmin cca = new ClientCommunicatorAdmin(50) {
+
+ @Override
+ protected void checkConnection() throws IOException {
+ // empty
+ }
+
+ @Override
+ protected void doStart() throws IOException {
+ if (!beingReconnected.compareAndSet(false, true)) {
+ IOException e = new IOException("Detected overlayed reconnect requests");
+ thrownExceptions.add(e);
+ throw e;
+ }
+ try {
+ Thread.sleep(800); // simulating a workload
+ beingReconnected.set(false);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ @Override
+ protected void doStop() {
+ // empty
+ }
+ };
+
+ Runnable r = new Runnable() {
+ final private IOException e = new IOException("Forced reconnect");
+ @Override
+ public void run() {
+ try {
+ // forcing the reconnect request
+ cca.gotIOException(e);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ };
+
+ System.out.println(": Spawning " + THREAD_COUNT + " concurrent reconnect requests");
+
+ for(int i=0;i<THREAD_COUNT;i++) {
+ e.execute(r);
+ }
+
+ Thread.sleep(THREAD_COUNT * 1000);
+ e.shutdown();
+ e.awaitTermination(10, TimeUnit.SECONDS);
+
+ cca.terminate();
+
+ for(Exception thrown : thrownExceptions) {
+ throw thrown;
+ }
+ System.out.println(": Requests processed successfully");
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/Client.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,47 @@
+
+import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchService;
+import javax.management.MBeanServerConnection;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class Client {
+ public static void main(String[] argv) throws Exception {
+ if (argv.length != 1) throw new IllegalArgumentException("Expecting exactly one jmx url argument");
+
+ JMXServiceURL serverUrl = new JMXServiceURL(argv[0]);
+
+ ObjectName name = new ObjectName("test", "foo", "bar");
+ JMXConnector jmxConnector = JMXConnectorFactory.connect(serverUrl);
+ System.out.println("client connected");
+ jmxConnector.addConnectionNotificationListener(new NotificationListener() {
+ public void handleNotification(Notification notification, Object handback) {
+ System.err.println("no!" + notification);
+ }
+ }, null, null);
+ MBeanServerConnection jmxServer = jmxConnector.getMBeanServerConnection();
+
+ jmxServer.addNotificationListener(name, new NotificationListener() {
+ public void handleNotification(Notification notification, Object handback) {
+ System.out.println("client got:" + notification);
+ }
+ }, null, null);
+
+ for(int i=0;i<10;i++) {
+ System.out.println("client invoking foo");
+ jmxServer.invoke(name, "foo", new Object[]{}, new String[]{});
+ Thread.sleep(50);
+ }
+
+ System.err.println("happy!");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/ConfigKey.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,3 @@
+public enum ConfigKey {
+ CONSTANT3, CONSTANT2;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Client/TestNotification.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,25 @@
+
+import javax.management.Notification;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ *
+ * @author Jaroslav Bachorik <jaroslav.bachorik at oracle.com>
+ */
+public class TestNotification extends Notification {
+ private ConfigKey key;
+
+ public TestNotification(String type, Object source, long sequenceNumber) {
+ super(type, source, sequenceNumber);
+ key = ConfigKey.CONSTANT3;
+ }
+
+ @Override
+ public String toString() {
+ return "TestNotification{" + "key=" + key + '}';
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/ConfigKey.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,3 @@
+public enum ConfigKey {
+ CONSTANT1, CONSTANT2;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/Server.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,46 @@
+import java.lang.management.ManagementFactory;
+import java.net.BindException;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.server.ExportException;
+import java.util.Random;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class Server {
+ public static void main(String[] argv) throws Exception {
+ int serverPort = 12345;
+ ObjectName name = new ObjectName("test", "foo", "bar");
+ MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
+ SteMBean bean = new Ste();
+ jmxServer.registerMBean(bean, name);
+ boolean exported = false;
+ Random rnd = new Random(System.currentTimeMillis());
+ do {
+ try {
+ LocateRegistry.createRegistry(serverPort);
+ exported = true;
+ } catch (ExportException ee) {
+ if (ee.getCause() instanceof BindException) {
+ serverPort = rnd.nextInt(10000) + 4096;
+ } else {
+ throw ee;
+ }
+ }
+
+ } while (!exported);
+ JMXServiceURL serverUrl = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + serverPort + "/test");
+ JMXConnectorServer jmxConnector = JMXConnectorServerFactory.newJMXConnectorServer(serverUrl, null, jmxServer);
+ jmxConnector.start();
+ System.out.println(serverUrl);
+ System.err.println("server listening on " + serverUrl);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/Ste.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,10 @@
+
+import javax.management.NotificationBroadcasterSupport;
+
+public class Ste extends NotificationBroadcasterSupport implements SteMBean {
+ private long count = 0;
+
+ public void foo() {
+ sendNotification(new TestNotification("test", this, count++));
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/SteMBean.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,3 @@
+public interface SteMBean {
+ public void foo();
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/Server/TestNotification.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,25 @@
+
+import javax.management.Notification;
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ *
+ * @author Jaroslav Bachorik <jaroslav.bachorik at oracle.com>
+ */
+public class TestNotification extends Notification {
+ private ConfigKey key;
+
+ public TestNotification(String type, Object source, long sequenceNumber) {
+ super(type, source, sequenceNumber);
+ key = ConfigKey.CONSTANT1;
+ }
+
+ @Override
+ public String toString() {
+ return "TestNotification{" + "key=" + key + '}';
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jmx/remote/NotificationMarshalVersions/TestSerializationMismatch.sh Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,93 @@
+#
+# Copyright (c) 2005, 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
+# @summary Tests for the RMI unmarshalling errors not to cause silent failure.
+# @author Jaroslav Bachorik
+# @bug 6937053
+#
+# @run shell TestSerializationMismatch.sh
+#
+
+#set -x
+
+#Set appropriate jdk
+#
+
+if [ ! -z "${TESTJAVA}" ] ; then
+ jdk="$TESTJAVA"
+else
+ echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test."
+ exit 1
+fi
+
+SERVER_TESTCLASSES=$TESTCLASSES/Server
+CLIENT_TESTCLASSES=$TESTCLASSES/Client
+
+URL_PATH=$SERVER_TESTCLASSES/jmxurl
+
+rm $URL_PATH
+
+mkdir -p $SERVER_TESTCLASSES
+mkdir -p $CLIENT_TESTCLASSES
+
+$TESTJAVA/bin/javac -d $CLIENT_TESTCLASSES $TESTSRC/Client/ConfigKey.java $TESTSRC/Client/TestNotification.java $TESTSRC/Client/Client.java
+$TESTJAVA/bin/javac -d $SERVER_TESTCLASSES $TESTSRC/Server/ConfigKey.java $TESTSRC/Server/TestNotification.java $TESTSRC/Server/SteMBean.java $TESTSRC/Server/Ste.java $TESTSRC/Server/Server.java
+
+startServer()
+{
+ ($TESTJAVA/bin/java -classpath $SERVER_TESTCLASSES Server) 1>$URL_PATH &
+ SERVER_PID=$!
+}
+
+runClient()
+{
+ while true
+ do
+ [ -f $URL_PATH ] && break
+ sleep 2
+ done
+ read JMXURL < $URL_PATH
+
+ HAS_ERRORS=`($TESTJAVA/bin/java -classpath $CLIENT_TESTCLASSES Client $JMXURL) 2>&1 | grep -i "SEVERE: Failed to fetch notification, stopping thread. Error is: java.rmi.UnmarshalException"`
+}
+
+startServer
+
+runClient
+
+sleep 1 # wait for notifications to arrive
+
+kill "$SERVER_PID"
+
+if [ -z "$HAS_ERRORS" ]
+then
+ echo "Test PASSED"
+ exit 0
+fi
+
+echo "Test FAILED"
+echo $HAS_ERRORS 1>&2
+exit 1
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/reflect/Method/IsDefaultTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012 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 8005042
+ * @summary Check behavior of Method.isDefault
+ * @author Joseph D. Darcy
+ */
+
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+import java.util.*;
+
+public class IsDefaultTest {
+ public static void main(String... argv) throws Exception {
+ int failures = 0;
+ int visitationCount = 0;
+
+ List<Class<?>> classList = new ArrayList<>();
+ classList.add(TestType1.class);
+ classList.add(TestType2.class);
+ classList.add(TestType3.class);
+ classList.add(TestType4.class);
+
+ for(Class<?> clazz: classList) {
+ for(Method method: clazz.getDeclaredMethods()) {
+ ExpectedIsDefault expectedIsDefault = method.getAnnotation(ExpectedIsDefault.class);
+ if (expectedIsDefault != null) {
+ visitationCount++;
+ boolean expected = expectedIsDefault.value();
+ boolean actual = method.isDefault();
+
+ if (actual != expected) {
+ failures++;
+ System.err.printf("ERROR: On %s expected isDefault of ''%s''; got ''%s''.\n",
+ method.toString(), expected, actual);
+ }
+ }
+ }
+ }
+
+ if (visitationCount == 0) {
+ System.err.println("Test failed because no methods checked.");
+ throw new RuntimeException();
+ }
+
+ if (failures > 0) {
+ System.err.println("Test failed.");
+ throw new RuntimeException();
+ }
+ }
+}
+
+interface TestType1 {
+ @ExpectedIsDefault(false)
+ void foo();
+
+ @ExpectedIsDefault(true)
+ default void bar() {}; // Default method
+}
+
+class TestType2 {
+ @ExpectedIsDefault(false)
+ void bar() {};
+}
+
+class TestType3 implements TestType1 {
+ @ExpectedIsDefault(false)
+ public void foo(){}
+
+ @ExpectedIsDefault(false)
+ @Override
+ public void bar() {};
+}
+
+@interface TestType4 {
+ @ExpectedIsDefault(false)
+ String value();
+
+ @ExpectedIsDefault(false)
+ String anotherValue() default "";
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface ExpectedIsDefault {
+ boolean value();
+}
--- a/jdk/test/java/rmi/testlibrary/JavaVM.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/java/rmi/testlibrary/JavaVM.java Wed Dec 26 10:08:36 2012 -0500
@@ -21,13 +21,10 @@
* questions.
*/
-/**
- *
- */
-
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.Arrays;
-import java.util.Properties;
import java.util.StringTokenizer;
/**
@@ -45,10 +42,6 @@
private OutputStream errorStream = System.err;
private String policyFileName = null;
- // This is used to shorten waiting time at startup.
- private volatile boolean started = false;
- private boolean forcesOutput = true; // default behavior
-
private static void mesg(Object mesg) {
System.err.println("JAVAVM: " + mesg.toString());
}
@@ -82,24 +75,6 @@
this.errorStream = err;
}
- /* This constructor will instantiate a JavaVM object for which caller
- * can ask for forcing initial version output on child vm process
- * (if forcesVersionOutput is true), or letting the started vm behave freely
- * (when forcesVersionOutput is false).
- */
- public JavaVM(String classname,
- String options, String args,
- OutputStream out, OutputStream err,
- boolean forcesVersionOutput) {
- this(classname, options, args, out, err);
- this.forcesOutput = forcesVersionOutput;
- }
-
-
- public void setStarted() {
- started = true;
- }
-
// Prepends passed opts array to current options
public void addOptions(String[] opts) {
String newOpts = "";
@@ -139,7 +114,8 @@
*/
public void start() throws IOException {
- if (vm != null) return;
+ if (vm != null)
+ throw new IllegalStateException("JavaVM already started");
/*
* If specified, add option for policy file
@@ -151,18 +127,6 @@
addOptions(new String[] { getCodeCoverageOptions() });
- /*
- * If forcesOutput is true :
- * We force the new starting vm to output something so that we can know
- * when it is effectively started by redirecting standard output through
- * the next StreamPipe call (the vm is considered started when a first
- * output has been streamed out).
- * We do this by prepnding a "-showversion" option in the command line.
- */
- if (forcesOutput) {
- addOptions(new String[] {"-showversion"});
- }
-
StringTokenizer optionsTokenizer = new StringTokenizer(options);
StringTokenizer argsTokenizer = new StringTokenizer(args);
int optionsCount = optionsTokenizer.countTokens();
@@ -186,43 +150,8 @@
vm = Runtime.getRuntime().exec(javaCommand);
/* output from the execed process may optionally be captured. */
- StreamPipe.plugTogether(this, vm.getInputStream(), this.outputStream);
- StreamPipe.plugTogether(this, vm.getErrorStream(), this.errorStream);
-
- try {
- if (forcesOutput) {
- // Wait distant vm to start, by using waiting time slices of 100 ms.
- // Wait at most for 2secs, after it considers the vm to be started.
- final long vmStartSleepTime = 100;
- final int maxTrials = 20;
- int numTrials = 0;
- while (!started && numTrials < maxTrials) {
- numTrials++;
- Thread.sleep(vmStartSleepTime);
- }
-
- // Outputs running status of distant vm
- String message =
- "after " + (numTrials * vmStartSleepTime) + " milliseconds";
- if (started) {
- mesg("distant vm process running, " + message);
- }
- else {
- mesg("unknown running status of distant vm process, " + message);
- }
- }
- else {
- // Since we have no way to know if the distant vm is started,
- // we just consider the vm to be started after a 2secs waiting time.
- Thread.sleep(2000);
- mesg("distant vm considered to be started after a waiting time of 2 secs");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- mesg("Thread interrupted while checking if distant vm is started. Giving up check.");
- mesg("Distant vm state unknown");
- return;
- }
+ StreamPipe.plugTogether(vm.getInputStream(), this.outputStream);
+ StreamPipe.plugTogether(vm.getErrorStream(), this.errorStream);
}
public void destroy() {
--- a/jdk/test/java/rmi/testlibrary/StreamPipe.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/java/rmi/testlibrary/StreamPipe.java Wed Dec 26 10:08:36 2012 -0500
@@ -21,11 +21,10 @@
* questions.
*/
-/**
- *
- */
-
-import java.io.*;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.io.OutputStream;
/**
* Pipe output of one stream into input of another.
@@ -34,84 +33,46 @@
private InputStream in;
private OutputStream out;
- private String preamble;
- private JavaVM javaVM;
- private static Object lock = new Object();
+
+ private static Object countLock = new Object();
private static int count = 0;
-
- /* StreamPipe constructor : should only be called by plugTogether() method !!
- * If passed vm is not null :
- * - This is StreamPipe usage when streams to pipe come from a given
- * vm (JavaVM) process (the vm process must be started with a prefixed
- * "-showversion" option to be able to determine as soon as possible when
- * the vm process is started through the redirection of the streams).
- * There must be a close connection between the StreamPipe instance and
- * the JavaVM object on which a start() call has been done.
- * run() method will flag distant JavaVM as started.
- * If passed vm is null :
- * - We don't have control on the process which we want to redirect the passed
- * streams.
- * run() method will ignore distant process.
+ /**
+ * StreamPipe constructor : should only be called by plugTogether() method.
*/
- private StreamPipe(JavaVM vm, InputStream in, OutputStream out, String name) {
+ private StreamPipe(InputStream in, OutputStream out, String name) {
super(name);
this.in = in;
this.out = out;
- this.preamble = "# ";
- this.javaVM = vm;
}
- // Install redirection of passed InputStream and OutputStream from passed JavaVM
- // to this vm standard output and input streams.
- public static void plugTogether(JavaVM vm, InputStream in, OutputStream out) {
- String name = null;
+ /**
+ * Creates a StreamPipe thread that copies in to out and returns
+ * the created instance.
+ */
+ public static StreamPipe plugTogether(InputStream in, OutputStream out) {
+ String name;
- synchronized (lock) {
- name = "TestLibrary: StreamPipe-" + (count ++ );
+ synchronized (countLock) {
+ name = "java.rmi.testlibrary.StreamPipe-" + (count++);
}
- Thread pipe = new StreamPipe(vm, in, out, name);
+ StreamPipe pipe = new StreamPipe(in, out, name);
pipe.setDaemon(true);
pipe.start();
- }
-
- /* Redirects the InputStream and OutputStream passed by caller to this
- * vm standard output and input streams.
- * (we just have to use fully parametered plugTogether() call with a null
- * JavaVM input to do this).
- */
- public static void plugTogether(InputStream in, OutputStream out) {
- plugTogether(null, in, out);
+ return pipe;
}
// Starts redirection of streams.
public void run() {
- BufferedReader r = new BufferedReader(new InputStreamReader(in), 1);
- BufferedWriter w = new BufferedWriter(new OutputStreamWriter(out));
- byte[] buf = new byte[256];
-
try {
- String line;
+ byte[] buf = new byte[1024];
- /* This is to check that the distant vm has started,
- * if such a vm has been provided at construction :
- * - As soon as we can read something from r BufferedReader,
- * that means the distant vm is already started.
- * Thus we signal associated JavaVM object that it is now started.
- */
- if (((line = r.readLine()) != null) &&
- (javaVM != null)) {
- javaVM.setStarted();
- }
-
- // Redirects r on w.
- while (line != null) {
- w.write(preamble);
- w.write(line);
- w.newLine();
- w.flush();
- line = r.readLine();
+ while (true) {
+ int nr = in.read(buf);
+ if (nr == -1)
+ break;
+ out.write(buf, 0, nr);
}
} catch (InterruptedIOException iioe) {
// Thread interrupted during IO operation. Terminate StreamPipe.
@@ -121,5 +82,4 @@
e.printStackTrace();
}
}
-
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/Compatibility.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"
+ [<!ENTITY intEnt 'value3'> ]>
+<?PITarget PIContent?>
+<properties>
+<comment>Property With Other Encoding</comment>
+<entry key="Key1">value1</entry>
+<entry key="Key2"><![CDATA[<value2>]]></entry>
+<entry key="Key3">&intEnt;</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/CompatibilityTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012, 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 8005280 8004371
+ * @summary Compatibility test
+ * @run main CompatibilityTest
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider CompatibilityTest
+ */
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * This is a behavior compatibility test.
+ * Although not defined by the properties.dtd, the constructs
+ * in Compatibility.xml are supported by the regular JDK XML
+ * Provider.
+ *
+ * @author: Joe Wang
+ */
+public class CompatibilityTest {
+
+ public static void main(String[] args) {
+ testInternalDTD();
+ }
+
+ /*
+ * Not in the spec, but the constructs work with the current JDK
+ */
+ static void testInternalDTD() {
+ String src = System.getProperty("test.src");
+ if (src == null) {
+ src = ".";
+ }
+ loadPropertyFile(src + "/Compatibility.xml");
+ }
+
+ /*
+ * 'Store' the populated 'Property' with the specified 'Encoding Type' as an
+ * XML file. Retrieve the same XML file and 'load' onto a new 'Property' object.
+ */
+ static void loadPropertyFile(String filename) {
+ try (InputStream in = new FileInputStream(filename)) {
+ Properties prop = new Properties();
+ prop.loadFromXML(in);
+ verifyProperites(prop);
+ } catch (IOException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ /*
+ * This method verifies the first key-value with the original string.
+ */
+ static void verifyProperites(Properties prop) {
+ try {
+ for (String key : prop.stringPropertyNames()) {
+ String val = prop.getProperty(key);
+ if (key.equals("Key1")) {
+ if (!val.equals("value1")) {
+ fail("Key:" + key + "'s value: \nExpected: value1\nFound: " + val);
+ }
+ } else if (key.equals("Key2")) {
+ if (!val.equals("<value2>")) {
+ fail("Key:" + key + "'s value: \nExpected: <value2>\nFound: " + val);
+ }
+ } else if (key.equals("Key3")) {
+ if (!val.equals("value3")) {
+ fail("Key:" + key + "'s value: \nExpected: value3\nFound: " + val);
+ }
+ }
+ }
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ }
+
+ static void fail(String err) {
+ throw new RuntimeException(err);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/ConcurrentLoadAndStoreXML.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012, 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 8005281
+ * @summary Test that the Properties storeToXML and loadFromXML methods are
+ * thread safe
+ * @run main ConcurrentLoadAndStoreXML
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider ConcurrentLoadAndStoreXML
+
+ */
+
+import java.io.*;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class ConcurrentLoadAndStoreXML {
+
+ static final Random RAND = new Random();
+
+ static volatile boolean done;
+
+ /**
+ * Simple task that bashes on storeToXML and loadFromXML until the "done"
+ * flag is set.
+ */
+ static class Basher implements Callable<Void> {
+ final Properties props;
+
+ Basher(Properties props) {
+ this.props = props;
+ }
+
+ public Void call() throws IOException {
+ while (!done) {
+
+ // store as XML format
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ props.storeToXML(out, null, "UTF-8");
+
+ // load from XML format
+ Properties p = new Properties();
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ p.loadFromXML(in);
+
+ // check that the properties are as expected
+ if (!p.equals(props))
+ throw new RuntimeException("Properties not equal");
+ }
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ final int NTASKS = 4 + RAND.nextInt(4);
+
+ // create Bashers with Properties of random keys and values
+ Basher[] basher = new Basher[NTASKS];
+ for (int i=0; i<NTASKS; i++) {
+ Properties props = new Properties();
+ for (int j=0; j<RAND.nextInt(100); j++) {
+ String key = "k" + RAND.nextInt(1000);
+ String value = "v" + RAND.nextInt(1000);
+ props.put(key, value);
+ }
+ basher[i] = new Basher(props);
+ }
+
+ ExecutorService pool = Executors.newFixedThreadPool(NTASKS);
+ try {
+ // kick off the bashers
+ Future<Void>[] task = new Future[NTASKS];
+ for (int i=0; i<NTASKS; i++) {
+ task[i] = pool.submit(basher[i]);
+ }
+
+ // give them time to interfere with each each
+ Thread.sleep(2000);
+ done = true;
+
+ // check the result
+ for (int i=0; i<NTASKS; i++) {
+ task[i].get();
+ }
+ } finally {
+ done = true;
+ pool.shutdown();
+ }
+
+ }
+}
--- a/jdk/test/java/util/Properties/LoadAndStoreXML.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/java/util/Properties/LoadAndStoreXML.java Wed Dec 26 10:08:36 2012 -0500
@@ -23,13 +23,16 @@
/*
* @test
- * @bug 8000354 8000685
+ * @bug 8000354 8000685 8004371
* @summary Basic test of storeToXML and loadToXML
+ * @run main LoadAndStoreXML
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=jdk.internal.util.xml.BasicXmlPropertiesProvider LoadAndStoreXML
*/
import java.io.*;
import java.util.*;
import java.security.*;
+import java.nio.file.*;
public class LoadAndStoreXML {
@@ -67,9 +70,14 @@
* read with Properties#loadFromXML.
*/
static void testLoadAndStore(String encoding) throws IOException {
+ System.out.println("testLoadAndStore, encoding=" + encoding);
+
Properties props = new Properties();
props.put("k1", "foo");
props.put("k2", "bar");
+ props.put("k3", "\\u0020\\u0391\\u0392\\u0393\\u0394\\u0395\\u0396\\u0397");
+ props.put("k4", "\u7532\u9aa8\u6587");
+ props.put("k5", "<java.home>/lib/jaxp.properties");
ByteArrayOutputStream out = new ByteArrayOutputStream();
props.storeToXML(out, null, encoding);
@@ -89,6 +97,8 @@
* Test loadFromXML with a document that does not have an encoding declaration
*/
static void testLoadWithoutEncoding() throws IOException {
+ System.out.println("testLoadWithoutEncoding");
+
Properties expected = new Properties();
expected.put("foo", "bar");
@@ -107,10 +117,11 @@
}
}
- /**
- * Test loadFromXML with unsupported encoding
- */
- static void testLoadWithBadEncoding() throws IOException {
+ /**
+ * Test loadFromXML with unsupported encoding
+ */
+ static void testLoadWithBadEncoding() throws IOException {
+ System.out.println("testLoadWithBadEncoding");
String s = "<?xml version=\"1.0\" encoding=\"BAD\"?>" +
"<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
"<properties>" +
@@ -128,6 +139,7 @@
* Test storeToXML with unsupported encoding
*/
static void testStoreWithBadEncoding() throws IOException {
+ System.out.println("testStoreWithBadEncoding");
Properties props = new Properties();
props.put("foo", "bar");
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -137,6 +149,26 @@
} catch (UnsupportedEncodingException expected) { }
}
+ /**
+ * Test loadFromXML with malformed documents
+ */
+ static void testLoadWithMalformedDoc(Path dir) throws IOException {
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.xml")) {
+ for (Path file: stream) {
+ System.out.println("testLoadWithMalformedDoc, file=" + file.getFileName());
+ try (InputStream in = Files.newInputStream(file)) {
+ Properties props = new Properties();
+ try {
+ props.loadFromXML(in);
+ throw new RuntimeException("InvalidPropertiesFormatException not thrown");
+ } catch (InvalidPropertiesFormatException x) {
+ System.out.println(x);
+ }
+ }
+ }
+ }
+ }
+
public static void main(String[] args) throws IOException {
testLoadAndStore("UTF-8");
@@ -145,6 +177,12 @@
testLoadWithBadEncoding();
testStoreWithBadEncoding();
+ // malformed documents
+ String src = System.getProperty("test.src");
+ String subdir = "invalidxml";
+ Path dir = (src == null) ? Paths.get(subdir) : Paths.get(src, subdir);
+ testLoadWithMalformedDoc(dir);
+
// re-run sanity test with security manager
Policy orig = Policy.getPolicy();
Policy p = new SimplePolicy(new RuntimePermission("setSecurityManager"),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/BadCase.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML tags are case sensitve, opening and closing tags must use same case -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</PROPERTIES>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/BadDocType.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE namevaluepairs SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The root element for a XML properties document is properties -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/DTDRootNotMatch.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML tags are case sensitve, the root element should have been in lower case -->
+
+<Properties>
+<comment>comment</comment>
+<entry key="firstKey">value of the first key</entry>
+</Properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalComment.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The dtd allows zero or one comment element -->
+
+<properties>
+<comment>comment1</comment>
+<comment>comment2</comment>
+<entry key="firstKey">value of the first key</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalEntry.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- The dtd requires 'entry' element, 'Entry' is illegal -->
+
+<properties>
+<comment>comment</comment>
+<Entry key="firstKey">value of the first key</Entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalEntry1.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- 'entry1' is illegal as per the dtd -->
+
+<properties>
+<comment>comment1</comment>
+
+<entry key="firstkey">value of the first key</entry>
+<entry1 key="secondkey">value of the second key</entry1>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/IllegalKeyAttribute.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- 'key' attribute is expected. 'key1' is mistyped -->
+
+<properties>
+<comment>comment1</comment>
+<entry key1="typo">value of the first key</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoClosingTag.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML elements must having a closing tag -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoDocType.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!-- An XML properties document has the DOCTYPE declaration with the properties root element -->
+
+<properties>
+<entry key="foo">bar</entry>
+<entry key="gus">baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoNamespaceSupport.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- namespace is not supported -->
+
+<javautil:properties xmlns:javautil="http://java.sun.com/java/util/Properties">
+<javautil:comment>comment1</javautil:comment>
+
+<javautil:entry key="firstkey">value of the first key</javautil:entry>
+<javautil:entry key="secondkey">value of the second key</javautil:entry>
+</javautil:properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NoRoot.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML documents must have a root element -->
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/NotQuoted.xml Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
+
+<!-- XML attribute values must be quoted -->
+
+<properties>
+<entry key=foo>bar</entry>
+<entry key=gus>baz</entry>
+</properties>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/invalidxml/README.txt Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,5 @@
+
+This directory contains test cases for the LoadAndStoreXML test case. Each file
+in this directory should be a malformed XML document that should cause the
+java.util.Properties loadFromXML method to throw InvalidPropertiesFormatException.
+
--- a/jdk/test/java/util/prefs/PrefsSpi.sh Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/java/util/prefs/PrefsSpi.sh Wed Dec 26 10:08:36 2012 -0500
@@ -87,17 +87,17 @@
case "`uname`" in Windows*|CYGWIN* ) CPS=';';; *) CPS=':';; esac
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
-Djava.util.prefs.PreferencesFactory=StubPreferencesFactory \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "java.util.prefs.*"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES${CPS}extDir/PrefsSpi.jar" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
-Sys "$java" "${TESTVMOPTS}" "-cp" "$TESTCLASSES" "-Djava.ext.dirs=extDir" \
+Sys "$java" ${TESTVMOPTS} "-cp" "$TESTCLASSES" "-Djava.ext.dirs=extDir" \
-Djava.util.prefs.userRoot=. \
PrefsSpi "StubPreferences"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanInfo/SerializationTest1.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2005, 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 6783290
+ * @summary Test correct reading of an empty Descriptor.
+ * @author Jaroslav Bachorik
+ * @run clean SerializationTest1
+ * @run build SerializationTest1
+ * @run main SerializationTest1
+ */
+
+import java.io.*;
+import javax.management.*;
+
+public class SerializationTest1 {
+ public static void main(String[] args) throws Exception {
+ MBeanInfo mi1 = new MBeanInfo("",
+ "",
+ new MBeanAttributeInfo[]{},
+ new MBeanConstructorInfo[]{},
+ new MBeanOperationInfo[]{},
+ new MBeanNotificationInfo[]{},
+ ImmutableDescriptor.EMPTY_DESCRIPTOR);
+
+ test(mi1);
+
+ MBeanFeatureInfo mfi2 = new MBeanFeatureInfo("",
+ "",
+ ImmutableDescriptor.EMPTY_DESCRIPTOR);
+
+ test(mfi2);
+ }
+
+ public static void test(Object obj) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(obj);
+
+ final boolean[] failed = new boolean[]{false};
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream ois = new ObjectInputStream(bais) {
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ System.out.println("*** " + desc.getName());
+ if (desc.getName().equals("[Ljava.lang.Object;")) { // javax.management.Descriptor fields
+ Thread.dumpStack();
+ for(StackTraceElement e : Thread.currentThread().getStackTrace()) { // checking for the deserialization location
+ if (e.getMethodName().equals("skipCustomData")) { // indicates the presence of unread values from the custom object serialization
+ failed[0] = true;
+ }
+ }
+ }
+ return super.resolveClass(desc); //To change body of generated methods, choose Tools | Templates.
+ }
+ };
+ Object newObj = ois.readObject();
+
+ if (failed[0]) {
+ throw new RuntimeException("Zero-length descriptor not read back");
+ }
+ }
+}
--- a/jdk/test/javax/management/remote/mandatory/connection/AddressableTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/AddressableTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -45,19 +45,36 @@
private static final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
+ private static boolean isProtocolSupported(String protocol) {
+ if (protocol.equals("rmi"))
+ return true;
+ if (protocol.equals("iiop")) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ return true;
+ } catch (ClassNotFoundException x) { }
+ }
+ return false;
+ }
+
public static void main(String[] args) throws Exception {
System.out.println(">>> test the new interface Addressable.");
boolean ok = true;
for (int i = 0; i < protocols.length; i++) {
- try {
- test(protocols[i], prefixes[i]);
-
- System.out.println(">>> Test successed for "+protocols[i]);
- } catch (Exception e) {
- System.out.println(">>> Test failed for "+protocols[i]);
- e.printStackTrace(System.out);
- ok = false;
+ String protocol = protocols[i];
+ if (isProtocolSupported(protocol)) {
+ try {
+ test(protocol, prefixes[i]);
+ System.out.println(">>> Test successed for "+protocols[i]);
+ } catch (Exception e) {
+ System.out.println(">>> Test failed for "+protocols[i]);
+ e.printStackTrace(System.out);
+ ok = false;
+ }
+ } else {
+ System.out.format(">>> Test skipped for %s, protocol not supported%n",
+ protocol);
}
}
@@ -65,7 +82,7 @@
System.out.println(">>> All Test passed.");
} else {
System.out.println(">>> Some TESTs FAILED");
- System.exit(1);
+ throw new RuntimeException("See log for details");
}
}
--- a/jdk/test/javax/management/remote/mandatory/connection/CloseableTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/CloseableTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -42,7 +42,6 @@
import javax.management.remote.rmi.RMIIIOPServerImpl;
import javax.management.remote.rmi.RMIJRMPServerImpl;
import javax.management.remote.rmi.RMIServerImpl;
-import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
public class CloseableTest {
private static final Class closeArray[] = {
@@ -51,26 +50,43 @@
RMIConnection.class,
RMIConnectionImpl.class,
RMIConnectionImpl_Stub.class,
- _RMIConnection_Stub.class,
RMIServerImpl.class,
RMIIIOPServerImpl.class,
RMIJRMPServerImpl.class
};
+
+ static int error;
+
+ static void test(Class<?> c) {
+ System.out.println("\nTest " + c);
+ if (Closeable.class.isAssignableFrom(c)) {
+ System.out.println("Test passed!");
+ } else {
+ error++;
+ System.out.println("Test failed!");
+ }
+ }
+
+ static void test(String cn) {
+ try {
+ test(Class.forName(cn));
+ } catch (ClassNotFoundException ignore) {
+ System.out.println("\n" + cn + " not tested.");
+ }
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("Test that all the JMX Remote API classes that " +
"define\nthe method \"void close() throws " +
"IOException;\" extend\nor implement the " +
"java.io.Closeable interface.");
- int error = 0;
- for (Class c : closeArray) {
- System.out.println("\nTest " + c);
- if (Closeable.class.isAssignableFrom(c)) {
- System.out.println("Test passed!");
- } else {
- error++;
- System.out.println("Test failed!");
- }
+ for (Class<?> c : closeArray) {
+ test(c);
}
+
+ // Stub classes not present if RMI-IIOP not supported
+ test("org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub");
+
if (error > 0) {
final String msg = "\nTest FAILED! Got " + error + " error(s)";
System.out.println(msg);
--- a/jdk/test/javax/management/remote/mandatory/connection/ConnectionListenerNullTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/ConnectionListenerNullTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -39,33 +39,22 @@
import java.util.Map;
public class ConnectionListenerNullTest {
- static final boolean optionalFlag;
- static {
- Class genericClass = null;
+ static boolean isPresent(String cn) {
try {
- genericClass =
- Class.forName("javax.management.remote.generic.GenericConnector");
+ Class.forName(cn);
+ return true;
} catch (ClassNotFoundException x) {
- // NO optional package
+ return false;
}
- optionalFlag = (genericClass != null);
}
- final static String[] mandatoryList = {
- "service:jmx:rmi://", "service:jmx:iiop://"
- };
-
- final static String[] optionalList = {
- "service:jmx:jmxmp://"
- };
-
- public static int test(String[] urls) {
+ public static int test(String... urls) {
int errCount = 0;
for (int i=0;i<urls.length;i++) {
try {
final JMXServiceURL url = new JMXServiceURL(urls[i]);
final JMXConnector c =
- JMXConnectorFactory.newJMXConnector(url,(Map)null);
+ JMXConnectorFactory.newJMXConnector(url,(Map<String,String>)null);
final NotificationListener nl = null;
final NotificationFilter nf = null;
final Object h = null;
@@ -121,12 +110,19 @@
public static void main(String args[]) {
int errCount = 0;
- errCount += test(mandatoryList);
- if (optionalFlag) errCount += test(optionalList);
+
+ // mandatory
+ errCount += test("service:jmx:rmi://");
+
+ // optional
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ errCount += test("service:jmx:iiop://");
+ if (isPresent("javax.management.remote.generic.GenericConnector"))
+ errCount += test("service:jmx:jmxmp://");
+
if (errCount > 0) {
- System.err.println("ConnectionListenerNullTest failed: " +
- errCount + " error(s) reported.");
- System.exit(1);
+ throw new RuntimeException("ConnectionListenerNullTest failed: " +
+ errCount + " error(s) reported.");
}
System.out.println("ConnectionListenerNullTest passed.");
}
--- a/jdk/test/javax/management/remote/mandatory/connection/IIOPURLTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/IIOPURLTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -49,17 +49,24 @@
public static void main(String[] args) throws Exception {
JMXServiceURL inputAddr =
new JMXServiceURL("service:jmx:iiop://");
- JMXConnectorServer s =
- JMXConnectorServerFactory.newJMXConnectorServer(inputAddr, null,
- null);
+ JMXConnectorServer s;
+ try {
+ s = JMXConnectorServerFactory.newJMXConnectorServer(inputAddr, null, null);
+ } catch (java.net.MalformedURLException x) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ throw new RuntimeException("MalformedURLException thrown but iiop appears to be supported");
+ } catch (ClassNotFoundException expected) { }
+ System.out.println("IIOP protocol not supported, test skipped");
+ return;
+ }
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
mbs.registerMBean(s, new ObjectName("a:b=c"));
s.start();
JMXServiceURL outputAddr = s.getAddress();
if (!outputAddr.getURLPath().startsWith("/ior/IOR:")) {
- System.out.println("URL path should start with \"/ior/IOR:\": " +
- outputAddr);
- System.exit(1);
+ throw new RuntimeException("URL path should start with \"/ior/IOR:\": " +
+ outputAddr);
}
System.out.println("IIOP URL path looks OK: " + outputAddr);
JMXConnector c = JMXConnectorFactory.connect(outputAddr);
--- a/jdk/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -52,21 +52,27 @@
import com.sun.jmx.remote.util.EnvHelp;
public class IdleTimeoutTest {
+
+ static boolean isPresent(String cn) {
+ try {
+ Class.forName(cn);
+ return true;
+ } catch (ClassNotFoundException x) {
+ return false;
+ }
+ }
+
public static void main(String[] args) throws Exception {
boolean ok = true;
List protos;
if (args.length > 0)
protos = Arrays.asList(args);
else {
- protos =
- new ArrayList(Arrays.asList(new String[] {"rmi", "iiop"}));
- try {
- Class.forName("javax.management.remote.jmxmp." +
- "JMXMPConnectorServer");
+ protos = new ArrayList(Arrays.asList(new String[] {"rmi"}));
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ protos.add("iiop");
+ if (isPresent("javax.management.remote.jmxmp.JMXMPConnectorServer"))
protos.add("jmxmp");
- } catch (ClassNotFoundException e) {
- // OK: Optional JMXMP support is not present
- }
}
for (Iterator it = protos.iterator(); it.hasNext(); ) {
String proto = (String) it.next();
@@ -81,13 +87,13 @@
}
}
if (!ok) {
- System.out.println("SOME TESTS FAILED");
- System.exit(1);
+ throw new RuntimeException("Some tests failed - see log for details");
}
}
private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
- throws Exception {
+ throws Exception
+ {
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
server.start();
--- a/jdk/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -253,4 +253,3 @@
System.out.println("===Leave the method: " + m);
}
}
-
--- a/jdk/test/javax/management/remote/mandatory/connectorServer/SetMBeanServerForwarder.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/connectorServer/SetMBeanServerForwarder.java Wed Dec 26 10:08:36 2012 -0500
@@ -40,27 +40,16 @@
public class SetMBeanServerForwarder {
- static final boolean optionalFlag;
- static {
- Class genericClass = null;
+ static boolean isPresent(String cn) {
try {
- genericClass =
- Class.forName("javax.management.remote.generic.GenericConnector");
+ Class.forName(cn);
+ return true;
} catch (ClassNotFoundException x) {
- // NO optional package
+ return false;
}
- optionalFlag = (genericClass != null);
}
- final static String[] mandatoryList = {
- "service:jmx:rmi://", "service:jmx:iiop://"
- };
-
- final static String[] optionalList = {
- "service:jmx:jmxmp://"
- };
-
- public static int test(String[] urls) {
+ public static int test(String... urls) {
int errorCount = 0;
for (int i=0;i<urls.length;i++) {
try {
@@ -241,12 +230,19 @@
public static void main(String args[]) {
int errCount = 0;
- errCount += test(mandatoryList);
- if (optionalFlag) errCount += test(optionalList);
+
+ // mandatory
+ errCount += test("service:jmx:rmi://");
+
+ // optional
+ if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
+ errCount += test("service:jmx:iiop://");
+ if (isPresent("javax.management.remote.generic.GenericConnector"))
+ errCount += test("service:jmx:jmxmp://");
+
if (errCount > 0) {
- System.err.println("SetMBeanServerForwarder failed: " +
- errCount + " error(s) reported.");
- System.exit(1);
+ throw new RuntimeException("SetMBeanServerForwarder failed: " +
+ errCount + " error(s) reported.");
}
System.out.println("SetMBeanServerForwarder passed.");
}
--- a/jdk/test/javax/management/remote/mandatory/loading/MissingClassTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/loading/MissingClassTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -70,7 +70,6 @@
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
-import org.omg.CORBA.MARSHAL;
public class MissingClassTest {
private static final int NNOTIFS = 50;
@@ -84,6 +83,15 @@
private static final Object unserializableObject = Thread.currentThread();
+ private static boolean isInstance(Object o, String cn) {
+ try {
+ Class<?> c = Class.forName(cn);
+ return c.isInstance(o);
+ } catch (ClassNotFoundException x) {
+ return false;
+ }
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("Test that the client or server end of a " +
"connection does not fail if sent an object " +
@@ -118,8 +126,7 @@
if (ok)
System.out.println("Test passed");
else {
- System.out.println("TEST FAILED");
- System.exit(1);
+ throw new RuntimeException("TEST FAILED");
}
}
@@ -133,7 +140,7 @@
JMXConnectorServer cs;
JMXServiceURL url = new JMXServiceURL(proto, null, 0);
- Map serverMap = new HashMap();
+ Map<String,Object> serverMap = new HashMap<>();
serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
serverLoader);
@@ -151,7 +158,7 @@
}
cs.start();
JMXServiceURL addr = cs.getAddress();
- Map clientMap = new HashMap();
+ Map<String,Object> clientMap = new HashMap<>();
clientMap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
clientLoader);
@@ -174,7 +181,7 @@
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
- if (cause instanceof MARSHAL) // see CR 4935098
+ if (isInstance(cause, "org.omg.CORBA.MARSHAL")) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
@@ -188,7 +195,7 @@
}
System.out.println("Doing queryNames to ensure connection alive");
- Set names = mbsc.queryNames(null, null);
+ Set<ObjectName> names = mbsc.queryNames(null, null);
System.out.println("queryNames returned " + names);
System.out.println("Provoke exception of unknown class");
@@ -198,7 +205,7 @@
ok = false;
} catch (IOException e) {
Throwable wrapped = e.getCause();
- if (wrapped instanceof MARSHAL) // see CR 4935098
+ if (isInstance(wrapped, "org.omg.CORBA.MARSHAL")) // see CR 4935098
wrapped = wrapped.getCause();
if (wrapped instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
@@ -251,7 +258,7 @@
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
- if (cause instanceof MARSHAL) // see CR 4935098
+ if (isInstance(cause, "org.omg.CORBA.MARSHAL")) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException " +
@@ -584,15 +591,13 @@
try {
new ObjectOutputStream(new ByteArrayOutputStream())
.writeObject(tricky);
- System.out.println("TEST INCORRECT: tricky notif is " +
- "serializable");
- System.exit(1);
+ throw new RuntimeException("TEST INCORRECT: tricky notif is " +
+ "serializable");
} catch (NotSerializableException e) {
// OK: tricky notif is not serializable
} catch (IOException e) {
- System.out.println("TEST INCORRECT: tricky notif " +
- "serialization check failed");
- System.exit(1);
+ throw new RuntimeException("TEST INCORRECT: tricky notif " +
+ "serialization check failed");
}
/* Now shuffle an imaginary deck of cards where K, U, T, and
@@ -629,12 +634,11 @@
}
if (knownCount != 0 || unknownCount != 0
|| trickyCount != 0 || boringCount != 0) {
- System.out.println("TEST INCORRECT: Shuffle failed: " +
+ throw new RuntimeException("TEST INCORRECT: Shuffle failed: " +
"known=" + knownCount +" unknown=" +
unknownCount + " tricky=" + trickyCount +
" boring=" + boringCount +
" deal=" + notifList);
- System.exit(1);
}
String notifs = notifList.toString();
System.out.println("Shuffle: " + notifs);
@@ -646,10 +650,8 @@
case 't': n = tricky; break;
case 'b': n = boring; break;
default:
- System.out.println("TEST INCORRECT: Bad shuffle char: " +
- notifs.charAt(i));
- System.exit(1);
- throw new Error();
+ throw new RuntimeException("TEST INCORRECT: Bad shuffle char: " +
+ notifs.charAt(i));
}
sendNotification(n);
}
--- a/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/provider/ProviderTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -49,13 +49,13 @@
import provider.JMXConnectorProviderImpl;
import provider.JMXConnectorServerProviderImpl;
public class ProviderTest {
+
public static void main(String[] args) throws Exception {
System.out.println("Starting ProviderTest");
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
- //First do the test with a protocol handled by Service providers
- JMXServiceURL url =
- new JMXServiceURL("service:jmx:rmi://");
+ // First do the test with a protocol handled by Service providers
+ JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
dotest(url, mbs);
boolean clientCalled = provider.JMXConnectorProviderImpl.called();
@@ -66,16 +66,22 @@
System.out.println("Client provider not called");
if (!serverCalled)
System.out.println("Server provider not called");
- System.out.println("Test Failed");
- System.exit(1);
+ throw new RuntimeException("Test failed - see log for details");
}
- //The Service Provider doesn't handle IIOP. Default providers MUST
- //be called.
- url =
- new JMXServiceURL("service:jmx:iiop://");
-
- dotest(url, mbs);
+ // The Service Provider doesn't handle IIOP. Default providers MUST
+ // be called, which may or may not support IIOP.
+ url = new JMXServiceURL("service:jmx:iiop://");
+ try {
+ dotest(url, mbs);
+ } catch (MalformedURLException e) {
+ try {
+ Class.forName("javax.management.remote.rmi._RMIConnectionImpl_Tie");
+ e.printStackTrace(System.out);
+ throw new RuntimeException("MalformedURLException throw but IIOP appears to be supported");
+ } catch (ClassNotFoundException expected) { }
+ System.out.println("MalformedURLException thrown, IIOP transport not supported");
+ }
// Unsupported protocol.
JMXConnectorServer server = null;
@@ -87,31 +93,19 @@
JMXConnectorServerFactory.newJMXConnectorServer(url,
null,
mbs);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(MalformedURLException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch (MalformedURLException e) {
System.out.println("Expected MalformedURLException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
try {
client =
JMXConnectorFactory.newJMXConnector(url,
null);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(MalformedURLException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch (MalformedURLException e) {
System.out.println("Expected MalformedURLException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
//JMXConnectorProviderException
url =
@@ -121,60 +115,34 @@
JMXConnectorServerFactory.newJMXConnectorServer(url,
null,
mbs);
- System.out.println("Exception not thrown.");
- System.exit(1);
- }catch(JMXProviderException e) {
+ throw new RuntimeException("Exception not thrown.");
+ } catch(JMXProviderException e) {
System.out.println("Expected JMXProviderException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
try {
client =
JMXConnectorFactory.newJMXConnector(url,
null);
- System.out.println("Exception not thrown.");
- System.exit(1);
+ throw new RuntimeException("Exception not thrown.");
}catch(JMXProviderException e) {
System.out.println("Expected JMXProviderException thrown.");
}
- catch(Exception e) {
- e.printStackTrace();
- System.out.println("Exception thrown : " + e);
- System.exit(1);
- }
System.out.println("Test OK");
- return;
}
private static void dotest(JMXServiceURL url, MBeanServer mbs)
throws Exception {
JMXConnectorServer server = null;
JMXConnector client = null;
- try {
- server =
- JMXConnectorServerFactory.newJMXConnectorServer(url,
- null,
- mbs);
- }catch(IllegalArgumentException e) {
- e.printStackTrace();
- System.exit(1);
- }
+
+ server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
server.start();
JMXServiceURL outputAddr = server.getAddress();
System.out.println("Server started ["+ outputAddr+ "]");
- try {
- client =
- JMXConnectorFactory.newJMXConnector(outputAddr, null);
- }catch(IllegalArgumentException e) {
- e.printStackTrace();
- System.exit(1);
- }
+ client = JMXConnectorFactory.newJMXConnector(outputAddr, null);
client.connect();
System.out.println("Client connected");
--- a/jdk/test/javax/management/remote/mandatory/serverError/JMXServerErrorTest.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/javax/management/remote/mandatory/serverError/JMXServerErrorTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -120,7 +120,7 @@
try {
cs=JMXConnectorServerFactory.newJMXConnectorServer(jurl,null,kbs);
} catch (MalformedURLException m) {
- if ("jmxmp".equals(jurl.getProtocol())) {
+ if ("jmxmp".equals(jurl.getProtocol()) || "iiop".equals(jurl.getProtocol())) {
// OK, we may not have this in the classpath...
System.out.println("WARNING: Skipping protocol: " + jurl);
return;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JInternalFrame/5066752/bug5066752.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012, 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 5066752
+ @summary Transparent JDesktopPane impossible because isOpaque() returns true
+ @author mb50250: area=JDesktopPane
+ @library ../../regtesthelpers
+ @build Util
+ @run main bug5066752
+*/
+
+import java.awt.*;
+import javax.swing.*;
+import sun.awt.*;
+
+public class bug5066752
+{
+ private static JFrame frame;
+
+ public static void main(String[] args) throws Exception {
+ SunToolkit tk = (SunToolkit)Toolkit.getDefaultToolkit();
+ Robot r = new Robot();
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ createAndShowGUI();
+ }
+ });
+ tk.realSync();
+
+ r.delay(600);
+
+ Point p = Util.getCenterPoint(frame);
+ Color color = r.getPixelColor((int) p.getX(), (int) p.getY());
+ if (!color.equals(Color.RED)) {
+ throw new Exception("Test failed: JDesktopPane isn't transparent. Expected color is (red color): " + Color.RED + ", actual color is: " + color);
+ }
+ }
+
+ private static void createAndShowGUI() {
+ frame = new JFrame();
+
+ frame.setLayout(new BorderLayout());
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.setBackground(Color.RED);
+ frame.add(panel, BorderLayout.CENTER);
+
+ JDesktopPane dp = new JDesktopPane();
+ dp.setOpaque(false);
+ panel.add(dp, BorderLayout.CENTER);
+
+ frame.setBounds(200, 200, 300, 200);
+ frame.setVisible(true);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/RepaintManager/IconifyTest/IconifyTest.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012, 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 4665214
+ * @summary Makes sure that RepaintManager doesn't attempt to repaint
+ * a frame when it is iconified.
+ * @author Scott Violet
+ * @run main IconifyTest
+ */
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import sun.awt.*;
+
+public class IconifyTest {
+ private static volatile boolean windowIconifiedIsCalled = false;
+ private static volatile boolean frameIsRepainted = false;
+ static JFrame frame;
+ static JButton button;
+
+ public static void main(String[] args) throws Throwable {
+ SunToolkit toolkit = (SunToolkit) SunToolkit.getDefaultToolkit();
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ frame = new JFrame();
+ button = new JButton("HI");
+ frame.getContentPane().add(button);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowIconified(WindowEvent e) {
+ windowIconifiedIsCalled = true;
+ RepaintManager rm = RepaintManager.currentManager(null);
+ rm.paintDirtyRegions();
+ button.repaint();
+ if (!rm.getDirtyRegion(button).isEmpty()) {
+ frameIsRepainted = true;
+ }
+ }
+ });
+ frame.pack();
+ frame.setVisible(true);
+ }
+ });
+ toolkit.realSync();
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ frame.setExtendedState(Frame.ICONIFIED);
+ }
+ });
+ toolkit.realSync();
+
+ if (!windowIconifiedIsCalled) {
+ throw new Exception("Test failed: window was not iconified.");
+ }
+ if (frameIsRepainted) {
+ throw new Exception("Test failed: frame was repainted when window was iconified.");
+ }
+ }
+}
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopDoSomething.java Wed Dec 26 10:08:36 2012 -0500
@@ -41,7 +41,7 @@
System.err.println("Lock is too old. Aborting");
return;
}
- Thread.sleep(1);
+ Thread.sleep(500);
}
} catch (Throwable e) {
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh Wed Dec 26 10:08:36 2012 -0500
@@ -43,7 +43,7 @@
_compile(){
- if [ ! -e ${_testclasses} ]
+ if [ ! -d ${_testclasses} ]
then
mkdir -p ${_testclasses}
fi
@@ -53,7 +53,7 @@
# Compile testcase
${TESTJAVA}/bin/javac -d ${_testclasses} JMXStartStopDoSomething.java JMXStartStopTest.java
- if [ ! -e ${_testclasses}/JMXStartStopTest.class ]
+ if [ ! -f ${_testclasses}/JMXStartStopTest.class ]
then
echo "ERROR: Can't compile"
exit -1
@@ -63,15 +63,22 @@
_app_start(){
${TESTJAVA}/bin/java ${TESTVMOPTS} $* -cp ${_testclasses} JMXStartStopDoSomething >> ${_logname} 2>&1 &
- npid=`_get_pid`
- if [ "${npid}" = "" ]
- then
- echo "ERROR: Test app not started"
- if [ "${_jtreg}" = "yes" ]
+ x=0
+ while [ ! -f ${_lockFileName} ]
+ do
+ if [ $x -gt 20 ]
then
- exit -1
- fi
- fi
+ echo "ERROR: Test app not started"
+ if [ "${_jtreg}" = "yes" ]
+ then
+ exit -1
+ fi
+ fi
+
+ echo "Waiting JMXStartStopDoSomething to start: $x"
+ x=`expr $x + 1`
+ sleep 1
+ done
}
_get_pid(){
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/http/KeepAliveStream/InfiniteLoop.java Wed Dec 26 10:08:36 2012 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012, 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 8004863
+ * @summary Checks for proper close code in KeepAliveStream
+ */
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import java.io.*;
+import java.net.*;
+import java.util.concurrent.Phaser;
+
+// Racey test, will not always fail, but if it does then we have a problem.
+
+public class InfiniteLoop {
+
+ public static void main(String[] args) throws Exception {
+ HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
+ server.createContext("/test/InfiniteLoop", new RespHandler());
+ server.start();
+ try {
+ InetSocketAddress address = server.getAddress();
+ URL url = new URL("http://localhost:" + address.getPort()
+ + "/test/InfiniteLoop");
+ final Phaser phaser = new Phaser(2);
+ for (int i=0; i<10; i++) {
+ HttpURLConnection uc = (HttpURLConnection)url.openConnection();
+ final InputStream is = uc.getInputStream();
+ final Thread thread = new Thread() {
+ public void run() {
+ try {
+ phaser.arriveAndAwaitAdvance();
+ while (is.read() != -1)
+ Thread.sleep(50);
+ } catch (Exception x) { x.printStackTrace(); }
+ }};
+ thread.start();
+ phaser.arriveAndAwaitAdvance();
+ is.close();
+ System.out.println("returned from close");
+ thread.join();
+ }
+ } finally {
+ server.stop(0);
+ }
+ }
+
+ static class RespHandler implements HttpHandler {
+ static final int RESP_LENGTH = 32 * 1024;
+ @Override
+ public void handle(HttpExchange t) throws IOException {
+ InputStream is = t.getRequestBody();
+ byte[] ba = new byte[8192];
+ while(is.read(ba) != -1);
+
+ t.sendResponseHeaders(200, RESP_LENGTH);
+ try (OutputStream os = t.getResponseBody()) {
+ os.write(new byte[RESP_LENGTH]);
+ }
+ t.close();
+ }
+ }
+}
--- a/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java Wed Dec 26 10:07:00 2012 -0500
+++ b/jdk/test/sun/rmi/runtime/Log/6409194/NoConsoleOutput.java Wed Dec 26 10:08:36 2012 -0500
@@ -64,7 +64,7 @@
// (neither on standard output, nor on standard err streams).
JavaVM vm = new JavaVM(DoRMIStuff.class.getName(),
"-Djava.util.logging.config.file=" + loggingPropertiesFile,
- "", out, err, false);
+ "", out, err);
vm.start();
vm.getVM().waitFor();