diff options
author | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-08-14 23:12:35 +0000 |
---|---|---|
committer | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-08-14 23:12:35 +0000 |
commit | ffde862e033a0825e1e9972a89c0f1f80b261a8e (patch) | |
tree | 97037d2c09c8384d80531f67ec36a01205df6bdb /libjava/classpath/java | |
parent | b415ff10527e977c3758234fd930e2c027bfa17d (diff) | |
download | gcc-ffde862e033a0825e1e9972a89c0f1f80b261a8e.tar.gz |
2006-08-14 Mark Wielaard <mark@klomp.org>
Imported GNU Classpath 0.92
* HACKING: Add more importing hints. Update automake version
requirement.
* configure.ac (gconf-peer): New enable AC argument.
Add --disable-gconf-peer and --enable-default-preferences-peer
to classpath configure when gconf is disabled.
* scripts/makemake.tcl: Set gnu/java/util/prefs/gconf and
gnu/java/awt/dnd/peer/gtk to bc. Classify
gnu/java/security/Configuration.java as generated source file.
* gnu/java/lang/management/VMGarbageCollectorMXBeanImpl.java,
gnu/java/lang/management/VMMemoryPoolMXBeanImpl.java,
gnu/java/lang/management/VMClassLoadingMXBeanImpl.java,
gnu/java/lang/management/VMRuntimeMXBeanImpl.java,
gnu/java/lang/management/VMMemoryManagerMXBeanImpl.java,
gnu/java/lang/management/VMThreadMXBeanImpl.java,
gnu/java/lang/management/VMMemoryMXBeanImpl.java,
gnu/java/lang/management/VMCompilationMXBeanImpl.java: New VM stub
classes.
* java/lang/management/VMManagementFactory.java: Likewise.
* java/net/VMURLConnection.java: Likewise.
* gnu/java/nio/VMChannel.java: Likewise.
* java/lang/Thread.java (getState): Add stub implementation.
* java/lang/Class.java (isEnum): Likewise.
* java/lang/Class.h (isEnum): Likewise.
* gnu/awt/xlib/XToolkit.java (getClasspathTextLayoutPeer): Removed.
* javax/naming/spi/NamingManager.java: New override for StackWalker
functionality.
* configure, sources.am, Makefile.in, gcj/Makefile.in,
include/Makefile.in, testsuite/Makefile.in: Regenerated.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@116139 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/java')
163 files changed, 16368 insertions, 3908 deletions
diff --git a/libjava/classpath/java/awt/AWTEvent.java b/libjava/classpath/java/awt/AWTEvent.java index d10433cb3c3..a6151b424c1 100644 --- a/libjava/classpath/java/awt/AWTEvent.java +++ b/libjava/classpath/java/awt/AWTEvent.java @@ -103,6 +103,11 @@ public abstract class AWTEvent extends EventObject */ byte[] bdata; + /** + * Indicates if this event is dispatched by the KeyboardFocusManager. + */ + boolean isFocusManagerEvent = false; + /** Mask for selecting component events. */ public static final long COMPONENT_EVENT_MASK = 0x00001; diff --git a/libjava/classpath/java/awt/BasicStroke.java b/libjava/classpath/java/awt/BasicStroke.java index 3e259216fa7..160a3eb0f74 100644 --- a/libjava/classpath/java/awt/BasicStroke.java +++ b/libjava/classpath/java/awt/BasicStroke.java @@ -43,7 +43,6 @@ import gnu.java.awt.java2d.LineSegment; import gnu.java.awt.java2d.QuadSegment; import gnu.java.awt.java2d.Segment; -import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; @@ -118,6 +117,7 @@ public class BasicStroke implements Stroke /** The dash phase. */ private final float phase; + // The inner and outer paths of the stroke private Segment start, end; /** @@ -260,7 +260,7 @@ public class BasicStroke implements Stroke */ public Shape createStrokedShape(Shape s) { - PathIterator pi = s.getPathIterator( new AffineTransform() ); + PathIterator pi = s.getPathIterator(null); if( dash == null ) return solidStroke( pi ); @@ -435,8 +435,8 @@ public class BasicStroke implements Stroke else addSegments(p); - x = coords[0]; - y = coords[1]; + x = coords[2]; + y = coords[3]; break; case PathIterator.SEG_CUBICTO: @@ -452,17 +452,25 @@ public class BasicStroke implements Stroke else addSegments(p); - x = coords[0]; - y = coords[1]; + x = coords[4]; + y = coords[5]; break; case PathIterator.SEG_CLOSE: - p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width/2.0); - addSegments(p); + if (x == x0 && y == y0) + { + joinSegments(new Segment[] { start.first, end.first }); + } + else + { + p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width / 2.0); + addSegments(p); + } convertPath(output, start); convertPath(output, end); start = end = null; pathOpen = false; + output.setWindingRule(GeneralPath.WIND_EVEN_ODD); break; } pi.next(); @@ -499,7 +507,7 @@ public class BasicStroke implements Stroke } /** - * Convert and add the linked list of Segments in s to a GeneralPath p. + * Append the Segments in s to the GeneralPath p */ private void convertPath(GeneralPath p, Segment s) { @@ -527,18 +535,28 @@ public class BasicStroke implements Stroke p.closePath(); } - + /** - * Add to segments to start and end, joining the outer pair and + * Add the segments to start and end (the inner and outer edges of the stroke) */ private void addSegments(Segment[] segments) { - double[] p0 = start.last.last(); + joinSegments(segments); + start.add(segments[0]); + end.add(segments[1]); + } + + private void joinSegments(Segment[] segments) + { + double[] p0 = start.last.cp2(); double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()}; - double[] p2 = new double[]{segments[0].P1.getX(), segments[0].P1.getY()}; - double[] p3 = segments[0].first(); + double[] p2 = new double[]{segments[0].first.P1.getX(), segments[0].first.P1.getY()}; + double[] p3 = segments[0].cp1(); Point2D p; + p = lineIntersection(p0[0],p0[1],p1[0],p1[1], + p2[0],p2[1],p3[0],p3[1], false); + double det = (p1[0] - p0[0])*(p3[1] - p2[1]) - (p3[0] - p2[0])*(p1[1] - p0[1]); @@ -546,42 +564,14 @@ public class BasicStroke implements Stroke { // start and segment[0] form the 'inner' part of a join, // connect the overlapping segments - p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], false); - if( p == null ) - { - // Dodgy. - start.add(new LineSegment(start.last.P2, segments[0].P1)); - p = new Point2D.Double((segments[0].P1.getX()+ start.last.P2.getX())/2.0, - (segments[0].P1.getY()+ start.last.P2.getY())/2.0); - } - else - segments[0].P1 = start.last.P2 = p; - - start.add( segments[0] ); - joinSegments(end, segments[1], p); + joinInnerSegments(start, segments[0], p); + joinOuterSegments(end, segments[1], p); } else { // end and segment[1] form the 'inner' part - p0 = end.last.last(); - p1 = new double[]{end.last.P2.getX(), end.last.P2.getY()}; - p2 = new double[]{segments[1].P1.getX(), segments[1].P1.getY()}; - p3 = segments[1].first(); - - p = lineIntersection(p0[0],p0[1],p1[0],p1[1], - p2[0],p2[1],p3[0],p3[1], false); - if( p == null ) - { - // Dodgy. - end.add(new LineSegment(end.last.P2, segments[1].P1)); - p = new Point2D.Double((segments[1].P1.getX()+ end.last.P2.getX())/2.0, - (segments[1].P1.getY()+ end.last.P2.getY())/2.0); - } - else - segments[1].P1 = end.last.P2 = p; - - end.add( segments[1] ); - joinSegments(start, segments[0], p); + joinInnerSegments(end, segments[1], p); + joinOuterSegments(start, segments[0], p); } } @@ -602,7 +592,7 @@ public class BasicStroke implements Stroke break; case CAP_SQUARE: - p0 = a.last.last(); + p0 = a.last.cp2(); p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; dx = p1[0] - p0[0]; dy = p1[1] - p0[1]; @@ -617,7 +607,7 @@ public class BasicStroke implements Stroke break; case CAP_ROUND: - p0 = a.last.last(); + p0 = a.last.cp2(); p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; dx = p1[0] - p0[0]; dy = p1[1] - p0[1]; @@ -676,7 +666,7 @@ public class BasicStroke implements Stroke * insideP is the inside intersection point of the join, needed for * calculating miter lengths. */ - private void joinSegments(Segment a, Segment b, Point2D insideP) + private void joinOuterSegments(Segment a, Segment b, Point2D insideP) { double[] p0, p1; double dx, dy, l; @@ -685,10 +675,10 @@ public class BasicStroke implements Stroke switch( join ) { case JOIN_MITER: - p0 = a.last.last(); + p0 = a.last.cp2(); p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; double[] p2 = new double[]{b.P1.getX(), b.P1.getY()}; - double[] p3 = b.first(); + double[] p3 = b.cp1(); Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true); if( p == null || insideP == null ) a.add(new LineSegment(a.last.P2, b.P1)); @@ -705,7 +695,7 @@ public class BasicStroke implements Stroke break; case JOIN_ROUND: - p0 = a.last.last(); + p0 = a.last.cp2(); p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; dx = p1[0] - p0[0]; dy = p1[1] - p0[1]; @@ -715,7 +705,7 @@ public class BasicStroke implements Stroke c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); p0 = new double[]{b.P1.getX(), b.P1.getY()}; - p1 = b.first(); + p1 = b.cp1(); dx = p0[0] - p1[0]; // backwards direction. dy = p0[1] - p1[1]; @@ -730,6 +720,29 @@ public class BasicStroke implements Stroke a.add(new LineSegment(a.last.P2, b.P1)); break; } - a.add(b); } -} + + /** + * Join a and b segments, removing any overlap + */ + private void joinInnerSegments(Segment a, Segment b, Point2D p) + { + double[] p0 = a.last.cp2(); + double[] p1 = new double[] { a.last.P2.getX(), a.last.P2.getY() }; + double[] p2 = new double[] { b.P1.getX(), b.P1.getY() }; + double[] p3 = b.cp1(); + + if (p == null) + { + // Dodgy. + a.add(new LineSegment(a.last.P2, b.P1)); + p = new Point2D.Double((b.P1.getX() + a.last.P2.getX()) / 2.0, + (b.P1.getY() + a.last.P2.getY()) / 2.0); + } + else + // This assumes segments a and b are single segments, which is + // incorrect - if they are a linked list of segments (ie, passed in + // from a flattening operation), this produces strange results!! + a.last.P2 = b.P1 = p; + } +} diff --git a/libjava/classpath/java/awt/Canvas.java b/libjava/classpath/java/awt/Canvas.java index b599582ba93..843fded44db 100644 --- a/libjava/classpath/java/awt/Canvas.java +++ b/libjava/classpath/java/awt/Canvas.java @@ -68,6 +68,11 @@ public class Canvas * Compatible with Sun's JDK. */ private static final long serialVersionUID = -2284879212465893870L; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_canvas_number; /** * The graphics configuration associated with the canvas. @@ -343,4 +348,19 @@ public class Canvas /* Call the paint method */ paint(graphics); } + + /** + * Generate a unique name for this <code>Canvas</code>. + * + * @return A unique name for this <code>Canvas</code>. + */ + String generateName() + { + return "canvas" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_canvas_number++; + } } diff --git a/libjava/classpath/java/awt/CardLayout.java b/libjava/classpath/java/awt/CardLayout.java index 8b3fea2ca23..fcb05215af9 100644 --- a/libjava/classpath/java/awt/CardLayout.java +++ b/libjava/classpath/java/awt/CardLayout.java @@ -350,6 +350,7 @@ public class CardLayout implements LayoutManager2, Serializable } } ((Component) target).setVisible (true); + parent.validate(); } } diff --git a/libjava/classpath/java/awt/CheckboxMenuItem.java b/libjava/classpath/java/awt/CheckboxMenuItem.java index 197065f6535..2df621b71b7 100644 --- a/libjava/classpath/java/awt/CheckboxMenuItem.java +++ b/libjava/classpath/java/awt/CheckboxMenuItem.java @@ -63,6 +63,11 @@ public class CheckboxMenuItem extends MenuItem * Static Variables */ +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_chkmenuitem_number; + // Serialization constant private static final long serialVersionUID = 6190621106981774043L; @@ -352,6 +357,21 @@ paramString() accessibleContext = new AccessibleAWTCheckboxMenuItem(); return accessibleContext; } + + /** + * Generate a unique name for this <code>CheckboxMenuItem</code>. + * + * @return A unique name for this <code>CheckboxMenuItem</code>. + */ + String generateName() + { + return "chkmenuitem" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_chkmenuitem_number++; + } } // class CheckboxMenuItem diff --git a/libjava/classpath/java/awt/Choice.java b/libjava/classpath/java/awt/Choice.java index 90a8d3141c8..f1da94bbeb8 100644 --- a/libjava/classpath/java/awt/Choice.java +++ b/libjava/classpath/java/awt/Choice.java @@ -63,6 +63,11 @@ public class Choice extends Component * Static Variables */ +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_choice_number; + // Serialization constant private static final long serialVersionUID = -4075310674757313071L; @@ -639,4 +644,19 @@ paramString() accessibleContext = new AccessibleAWTChoice(); return accessibleContext; } + + /** + * Generate a unique name for this <code>Choice</code>. + * + * @return A unique name for this <code>Choice</code>. + */ + String generateName() + { + return "choice" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_choice_number++; + } } // class Choice diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java index 3d3dcc319cc..44f277ac783 100644 --- a/libjava/classpath/java/awt/Component.java +++ b/libjava/classpath/java/awt/Component.java @@ -70,6 +70,7 @@ import java.awt.image.ImageProducer; import java.awt.image.VolatileImage; import java.awt.peer.ComponentPeer; import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; @@ -213,6 +214,12 @@ public abstract class Component */ static final Object treeLock = new String("AWT_TREE_LOCK"); + /** + * The default maximum size. + */ + private static final Dimension DEFAULT_MAX_SIZE + = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); + // Serialized fields from the serialization spec. /** @@ -427,6 +434,24 @@ public abstract class Component Dimension minSize; /** + * Flag indicating whether the minimum size for the component has been set + * by a call to {@link #setMinimumSize(Dimension)} with a non-null value. + */ + boolean minSizeSet; + + /** + * The maximum size for the component. + * @see #setMaximumSize(Dimension) + */ + Dimension maxSize; + + /** + * A flag indicating whether the maximum size for the component has been set + * by a call to {@link #setMaximumSize(Dimension)} with a non-null value. + */ + boolean maxSizeSet; + + /** * Cached information on the preferred size. Should have been transient. * * @serial ignore @@ -434,6 +459,12 @@ public abstract class Component Dimension prefSize; /** + * Flag indicating whether the preferred size for the component has been set + * by a call to {@link #setPreferredSize(Dimension)} with a non-null value. + */ + boolean prefSizeSet; + + /** * Set to true if an event is to be handled by this component, false if * it is to be passed up the hierarcy. * @@ -563,6 +594,17 @@ public abstract class Component transient BufferStrategy bufferStrategy; /** + * The number of hierarchy listeners of this container plus all of its + * children. This is needed for efficient handling of HierarchyEvents. + * These must be propagated to all child components with HierarchyListeners + * attached. To avoid traversal of the whole subtree, we keep track of + * the number of HierarchyListeners here and only walk the paths that + * actually have listeners. + */ + int numHierarchyListeners; + int numHierarchyBoundsListeners; + + /** * true if requestFocus was called on this component when its * top-level ancestor was not focusable. */ @@ -607,16 +649,19 @@ public abstract class Component } /** - * Sets the name of this component to the specified name. + * Sets the name of this component to the specified name (this is a bound + * property with the name 'name'). * - * @param name the new name of this component + * @param name the new name (<code>null</code> permitted). * @see #getName() * @since 1.1 */ public void setName(String name) { nameExplicitlySet = true; + String old = this.name; this.name = name; + firePropertyChange("name", old, name); } /** @@ -718,7 +763,9 @@ public abstract class Component */ public boolean isValid() { - return valid; + // Tests show that components are invalid as long as they are not showing, even after validate() + // has been called on them. + return peer != null && valid; } /** @@ -804,9 +851,17 @@ public abstract class Component */ public void enable() { - this.enabled = true; - if (peer != null) - peer.setEnabled (true); + if (! enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = true; + ComponentPeer p = peer; + if (p != null) + p.enable(); + } + } } /** @@ -831,9 +886,17 @@ public abstract class Component */ public void disable() { - this.enabled = false; - if (peer != null) - peer.setEnabled (false); + if (enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = false; + ComponentPeer p = peer; + if (p != null) + p.disable(); + } + } } /** @@ -898,16 +961,38 @@ public abstract class Component // and its children. if(!isVisible()) { - this.visible = true; - // Avoid NullPointerExceptions by creating a local reference. - ComponentPeer currentPeer=peer; - if (currentPeer != null) - currentPeer.show(); - - // The JDK repaints the component before invalidating the parent. - // So do we. - if (isShowing() && isLightweight()) - repaint(); + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = true; + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer=peer; + if (currentPeer != null) + { + currentPeer.show(); + + // Fire HierarchyEvent. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + + // The JDK repaints the component before invalidating the parent. + // So do we. + if (isLightweight()) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + // Invalidate the parent if we have one. The component itself must // not be invalidated. We also avoid NullPointerException with // a local reference here. @@ -915,9 +1000,6 @@ public abstract class Component if (currentParent != null) currentParent.invalidate(); - ComponentEvent ce = - new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); - getToolkit().getSystemEventQueue().postEvent(ce); } } @@ -945,27 +1027,45 @@ public abstract class Component { if (isVisible()) { - // Avoid NullPointerExceptions by creating a local reference. - ComponentPeer currentPeer=peer; - if (currentPeer != null) - currentPeer.setVisible(false); - boolean wasShowing = isShowing(); - this.visible = false; - - // The JDK repaints the component before invalidating the parent. - // So do we. - if (wasShowing) - repaint(); - // Invalidate the parent if we have one. The component itself must + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = false; + + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer=peer; + if (currentPeer != null) + { + currentPeer.hide(); + + // Fire hierarchy event. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + // The JDK repaints the component before invalidating the + // parent. So do we. This only applies for lightweights. + if (peer instanceof LightweightPeer) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + // Invalidate the parent if we have one. The component itself need // not be invalidated. We also avoid NullPointerException with // a local reference here. Container currentParent = parent; if (currentParent != null) currentParent.invalidate(); - ComponentEvent ce = - new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); - getToolkit().getSystemEventQueue().postEvent(ce); } } @@ -1088,16 +1188,13 @@ public abstract class Component */ public void setFont(Font newFont) { - if((newFont != null && (font == null || !font.equals(newFont))) - || newFont == null) - { - Font oldFont = font; - font = newFont; - if (peer != null) - peer.setFont(font); - firePropertyChange("font", oldFont, newFont); - invalidate(); - } + Font oldFont = font; + font = newFont; + if (peer != null) + peer.setFont(font); + firePropertyChange("font", oldFont, newFont); + if (valid) + invalidate(); } /** @@ -1189,8 +1286,15 @@ public abstract class Component throw new IllegalComponentStateException("component " + getClass().getName() + " not showing"); - // We know peer != null here. - return peer.getLocationOnScreen(); + + // Need to lock the tree here. We get crazy races and explosions when + // the tree changes while we are trying to find the location of this + // component. + synchronized (getTreeLock()) + { + // We know peer != null here. + return peer.getLocationOnScreen(); + } } /** @@ -1384,53 +1488,98 @@ public abstract class Component */ public void reshape(int x, int y, int width, int height) { - int oldx = this.x; - int oldy = this.y; - int oldwidth = this.width; - int oldheight = this.height; + // We need to lock the tree here, otherwise we risk races and + // inconsistencies. + synchronized (getTreeLock()) + { + int oldx = this.x; + int oldy = this.y; + int oldwidth = this.width; + int oldheight = this.height; - if (this.x == x && this.y == y && this.width == width - && this.height == height) - return; + boolean resized = oldwidth != width || oldheight != height; + boolean moved = oldx != x || oldy != y; - invalidate(); - - this.x = x; - this.y = y; - this.width = width; - this.height = height; - if (peer != null) - peer.setBounds (x, y, width, height); - - // Erase old bounds and repaint new bounds for lightweights. - if (isLightweight() && isShowing()) - { - if (parent != null) + if (resized || moved) { - Rectangle oldBounds = new Rectangle(oldx, oldy, oldwidth, - oldheight); - Rectangle newBounds = new Rectangle(x, y, width, height); - Rectangle destroyed = oldBounds.union(newBounds); - if (!destroyed.isEmpty()) - parent.repaint(0, destroyed.x, destroyed.y, destroyed.width, - destroyed.height); + // Update the fields. + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + if (peer != null) + { + peer.setBounds (x, y, width, height); + if (resized) + invalidate(); + if (parent != null && parent.valid) + parent.invalidate(); + } + + // Send some events to interested listeners. + notifyReshape(resized, moved); + + // Repaint this component and the parent if appropriate. + if (parent != null && peer instanceof LightweightPeer + && isShowing()) + { + // The parent repaints the area that we occupied before. + parent.repaint(oldx, oldy, oldwidth, oldheight); + // This component repaints the area that we occupy now. + repaint(); + } } } + } - // Only post event if this component is visible and has changed size. - if (isShowing () - && (oldx != x || oldy != y)) + private void notifyReshape(boolean resized, boolean moved) + { + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) { - ComponentEvent ce = new ComponentEvent(this, - ComponentEvent.COMPONENT_MOVED); - getToolkit().getSystemEventQueue().postEvent(ce); + // Fire component event on this component. + if (moved) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_MOVED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + if (resized) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_RESIZED); + getToolkit().getSystemEventQueue().postEvent(ce); + } } - if (isShowing () - && (oldwidth != width || oldheight != height)) + else { - ComponentEvent ce = new ComponentEvent(this, - ComponentEvent.COMPONENT_RESIZED); - getToolkit().getSystemEventQueue().postEvent(ce); + // Otherwise we might need to notify child components when this is + // a Container. + if (this instanceof Container) + { + Container cont = (Container) this; + if (resized) + { + for (int i = 0; i < cont.getComponentCount(); i++) + { + Component child = cont.getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED, + this, parent, 0); + } + } + if (moved) + { + for (int i = 0; i < cont.getComponentCount(); i++) + { + Component child = cont.getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED, + this, parent, 0); + } + } + } } } @@ -1584,6 +1733,7 @@ public abstract class Component * * @return the component's preferred size * @see #getMinimumSize() + * @see #setPreferredSize(Dimension) * @see LayoutManager */ public Dimension getPreferredSize() @@ -1592,6 +1742,40 @@ public abstract class Component } /** + * Sets the preferred size that will be returned by + * {@link #getPreferredSize()} always, and sends a + * {@link PropertyChangeEvent} (with the property name 'preferredSize') to + * all registered listeners. + * + * @param size the preferred size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getPreferredSize() + */ + public void setPreferredSize(Dimension size) + { + Dimension old = prefSizeSet ? prefSize : null; + prefSize = size; + prefSizeSet = (size != null); + firePropertyChange("preferredSize", old, size); + } + + /** + * Returns <code>true</code> if the current preferred size is not + * <code>null</code> and was set by a call to + * {@link #setPreferredSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isPreferredSizeSet() + { + return prefSizeSet; + } + + /** * Returns the component's preferred size. * * @return the component's preferred size @@ -1599,14 +1783,36 @@ public abstract class Component */ public Dimension preferredSize() { - if (prefSize == null) + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(preferredSizeImpl()); + } + + /** + * The actual calculation is pulled out of preferredSize() so that + * we can call it from Container.preferredSize() and avoid creating a + * new intermediate Dimension object. + * + * @return the preferredSize of the component + */ + Dimension preferredSizeImpl() + { + Dimension size = prefSize; + // Try to use a cached value. + if (size == null || !(valid || prefSizeSet)) { - if (peer == null) - prefSize = minimumSize(); - else - prefSize = peer.getPreferredSize(); + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.preferredSize(); + else + size = minimumSizeImpl(); + } } - return prefSize; + return size; } /** @@ -1614,6 +1820,7 @@ public abstract class Component * * @return the component's minimum size * @see #getPreferredSize() + * @see #setMinimumSize(Dimension) * @see LayoutManager */ public Dimension getMinimumSize() @@ -1622,6 +1829,39 @@ public abstract class Component } /** + * Sets the minimum size that will be returned by {@link #getMinimumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'minimumSize') to all registered listeners. + * + * @param size the minimum size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getMinimumSize() + */ + public void setMinimumSize(Dimension size) + { + Dimension old = minSizeSet ? minSize : null; + minSize = size; + minSizeSet = (size != null); + firePropertyChange("minimumSize", old, size); + } + + /** + * Returns <code>true</code> if the current minimum size is not + * <code>null</code> and was set by a call to + * {@link #setMinimumSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMinimumSizeSet() + { + return minSizeSet; + } + + /** * Returns the component's minimum size. * * @return the component's minimum size @@ -1629,10 +1869,36 @@ public abstract class Component */ public Dimension minimumSize() { - if (minSize == null) - minSize = (peer != null ? peer.getMinimumSize() - : new Dimension(width, height)); - return minSize; + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(minimumSizeImpl()); + } + + /** + * The actual calculation is pulled out of minimumSize() so that + * we can call it from Container.preferredSize() and + * Component.preferredSizeImpl and avoid creating a + * new intermediate Dimension object. + * + * @return the minimum size of the component + */ + Dimension minimumSizeImpl() + { + Dimension size = minSize; + if (size == null || !(valid || minSizeSet)) + { + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.minimumSize(); + else + size = size(); + } + } + return size; } /** @@ -1640,15 +1906,66 @@ public abstract class Component * * @return the component's maximum size * @see #getMinimumSize() + * @see #setMaximumSize(Dimension) * @see #getPreferredSize() * @see LayoutManager */ public Dimension getMaximumSize() { - return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); + return new Dimension(maximumSizeImpl()); + } + + /** + * This is pulled out from getMaximumSize(), so that we can access it + * from Container.getMaximumSize() without creating an additional + * intermediate Dimension object. + * + * @return the maximum size of the component + */ + Dimension maximumSizeImpl() + { + Dimension size; + if (maxSizeSet) + size = maxSize; + else + size = DEFAULT_MAX_SIZE; + return size; + } + + /** + * Sets the maximum size that will be returned by {@link #getMaximumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'maximumSize') to all registered listeners. + * + * @param size the maximum size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getMaximumSize() + */ + public void setMaximumSize(Dimension size) + { + Dimension old = maxSizeSet ? maxSize : null; + maxSize = size; + maxSizeSet = (size != null); + firePropertyChange("maximumSize", old, size); } /** + * Returns <code>true</code> if the current maximum size is not + * <code>null</code> and was set by a call to + * {@link #setMaximumSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMaximumSizeSet() + { + return maxSizeSet; + } + + /** * Returns the preferred horizontal alignment of this component. The value * returned will be between {@link #LEFT_ALIGNMENT} and * {@link #RIGHT_ALIGNMENT}, inclusive. @@ -1716,11 +2033,25 @@ public abstract class Component */ public void invalidate() { - valid = false; - prefSize = null; - minSize = null; - if (parent != null && parent.isValid()) - parent.invalidate(); + // Need to lock here, to avoid races and other ugly stuff when doing + // layout or structure changes in other threads. + synchronized (getTreeLock()) + { + // Invalidate. + valid = false; + + // Throw away cached layout information. + if (! minSizeSet) + minSize = null; + if (! prefSizeSet) + prefSize = null; + if (! maxSizeSet) + maxSize = null; + + // Also invalidate the parent, if it hasn't already been invalidated. + if (parent != null && parent.isValid()) + parent.invalidate(); + } } /** @@ -1826,11 +2157,9 @@ public abstract class Component } /** - * Updates this component. This is called in response to - * <code>repaint</code>. This method fills the component with the - * background color, then sets the foreground color of the specified - * graphics context to the foreground color of this component and calls - * the <code>paint()</code> method. The coordinates of the graphics are + * Updates this component. This is called for heavyweight components in + * response to {@link #repaint()}. The default implementation simply forwards + * to {@link #paint(Graphics)}. The coordinates of the graphics are * relative to this component. Subclasses should call either * <code>super.update(g)</code> or <code>paint(g)</code>. * @@ -1838,27 +2167,17 @@ public abstract class Component * * @see #paint(Graphics) * @see #repaint() - * - * @specnote In contrast to what the spec says, tests show that the exact - * behaviour is to clear the background on lightweight and - * top-level components only. Heavyweight components are not - * affected by this method and only call paint(). */ public void update(Graphics g) { - // Tests show that the clearing of the background is only done in - // two cases: - // - If the component is lightweight (yes this is in contrast to the spec). - // or - // - If the component is a toplevel container. - if (isLightweight() || getParent() == null) - { - Rectangle clip = g.getClipBounds(); - if (clip == null) - g.clearRect(0, 0, width, height); - else - g.clearRect(clip.x, clip.y, clip.width, clip.height); - } + // Note 1: We used to clear the background here for lightweights and + // toplevel components. Tests show that this is not what the JDK does + // here. Note that there is some special handling and background + // clearing code in Container.update(Graphics). + + // Note 2 (for peer implementors): The JDK doesn't seem call update() for + // toplevel components, even when an UPDATE event is sent (as a result + // of repaint). paint(g); } @@ -1934,11 +2253,46 @@ public abstract class Component */ public void repaint(long tm, int x, int y, int width, int height) { - if (isShowing()) + // The repaint() call has previously been delegated to + // {@link ComponentPeer.repaint()}. Testing on the JDK using some + // dummy peers show that this methods is never called. I think it makes + // sense to actually perform the tasks below here, since it's pretty + // much peer independent anyway, and makes sure only heavyweights are + // bothered by this. + ComponentPeer p = peer; + + // Let the nearest heavyweight parent handle repainting for lightweight + // components. + // This goes up the hierarchy until we hit + // a heavyweight component that handles this and translates the + // rectangle while doing so. + + // We perform some boundary checking to restrict the paint + // region to this component. + int px = (x < 0 ? 0 : x); + int py = (y < 0 ? 0 : y); + int pw = width; + int ph = height; + Component par = this; + while (par != null && p instanceof LightweightPeer) + { + px += par.x; + py += par.y; + // We perform some boundary checking to restrict the paint + // region to this component. + pw = Math.min(pw, par.width); + ph = Math.min(ph, par.height); + par = par.parent; + p = par.peer; + } + + // Now send an UPDATE event to the heavyweight component that we've found. + if (par != null && par.isVisible() && p != null && pw > 0 && ph > 0) { - ComponentPeer p = peer; - if (p != null) - p.repaint(tm, x, y, width, height); + assert ! (p instanceof LightweightPeer); + PaintEvent pe = new PaintEvent(par, PaintEvent.UPDATE, + new Rectangle(px, py, pw, ph)); + getToolkit().getSystemEventQueue().postEvent(pe); } } @@ -1957,10 +2311,7 @@ public abstract class Component } /** - * Prints this component, including all sub-components. This method is - * provided so that printing can be done in a different manner from - * painting. However, the implementation in this class simply calls the - * <code>paintAll()</code> method. + * Prints this component, including all sub-components. * * @param g the graphics context of the print device * @@ -1968,7 +2319,9 @@ public abstract class Component */ public void printAll(Graphics g) { - paintAll(g); + if( peer != null ) + peer.print( g ); + paintAll( g ); } /** @@ -2318,6 +2671,17 @@ public abstract class Component } /** + * By default, no old mouse events should be ignored. + * This can be overridden by subclasses. + * + * @return false, no mouse events are ignored. + */ + static boolean ignoreOldMouseEvents() + { + return false; + } + + /** * AWT 1.0 event handler. * * This method simply calls handleEvent and returns the result. @@ -2449,6 +2813,14 @@ public abstract class Component hierarchyListener = AWTEventMulticaster.add(hierarchyListener, listener); if (hierarchyListener != null) enableEvents(AWTEvent.HIERARCHY_EVENT_MASK); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners++; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, 1); + } } /** @@ -2464,6 +2836,15 @@ public abstract class Component public synchronized void removeHierarchyListener(HierarchyListener listener) { hierarchyListener = AWTEventMulticaster.remove(hierarchyListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners--; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -1); + } } /** @@ -2499,6 +2880,16 @@ public abstract class Component AWTEventMulticaster.add(hierarchyBoundsListener, listener); if (hierarchyBoundsListener != null) enableEvents(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners++; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + 1); + } } /** @@ -2516,6 +2907,16 @@ public abstract class Component { hierarchyBoundsListener = AWTEventMulticaster.remove(hierarchyBoundsListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners--; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -1); + } } /** @@ -2534,6 +2935,40 @@ public abstract class Component } /** + * Fires a HierarchyEvent or HierarchyChangeEvent on this component. + * + * @param id the event id + * @param changed the changed component + * @param parent the parent + * @param flags the event flags + */ + void fireHierarchyEvent(int id, Component changed, Container parent, + long flags) + { + boolean enabled = false; + switch (id) + { + case HierarchyEvent.HIERARCHY_CHANGED: + enabled = hierarchyListener != null + || (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0; + break; + case HierarchyEvent.ANCESTOR_MOVED: + case HierarchyEvent.ANCESTOR_RESIZED: + enabled = hierarchyBoundsListener != null + || (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0; + break; + default: + assert false : "Should not reach here"; + } + if (enabled) + { + HierarchyEvent ev = new HierarchyEvent(this, id, changed, parent, + flags); + dispatchEvent(ev); + } + } + + /** * Adds the specified listener to this component. This is harmless if the * listener is null, but if the listener has already been registered, it * will now be registered twice. @@ -3435,18 +3870,24 @@ public abstract class Component */ public void addNotify() { - if (peer == null) - peer = getToolkit().createComponent(this); - else if (parent != null && parent.isLightweight()) - new HeavyweightInLightweightListener(parent); - /* Now that all the children has gotten their peers, we should + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + if (peer == null) + peer = getToolkit().createComponent(this); + else if (parent != null && parent.isLightweight()) + new HeavyweightInLightweightListener(parent); + /* Now that all the children has gotten their peers, we should have the event mask needed for this component and its lightweight subcomponents. */ - peer.setEventMask(eventMask); - /* We do not invalidate here, but rather leave that job up to + peer.setEventMask(eventMask); + /* We do not invalidate here, but rather leave that job up to the peer. For efficiency, the peer can choose not to invalidate if it is happy with the current dimensions, etc. */ + if (dropTarget != null) + dropTarget.addNotify(peer); + } } /** @@ -3460,17 +3901,21 @@ public abstract class Component */ public void removeNotify() { - // We null our peer field before disposing of it, such that if we're - // not the event dispatch thread and the dispatch thread is awoken by - // the dispose call, there will be no race checking the peer's null - // status. - - ComponentPeer tmp = peer; - peer = null; - if (tmp != null) + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) { - tmp.hide(); - tmp.dispose(); + // We null our peer field before disposing of it, such that if we're + // not the event dispatch thread and the dispatch thread is awoken by + // the dispose call, there will be no race checking the peer's null + // status. + + ComponentPeer tmp = peer; + peer = null; + if (tmp != null) + { + tmp.hide(); + tmp.dispose(); + } } } @@ -3791,56 +4236,7 @@ public abstract class Component */ public void requestFocus () { - if (isDisplayable () - && isShowing () - && isFocusable ()) - { - synchronized (getTreeLock ()) - { - // Find this Component's top-level ancestor. - Container parent = (this instanceof Container) ? (Container) this - : getParent(); - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - if (parent == null) - return; - - Window toplevel = (Window) parent; - if (toplevel.isFocusableWindow ()) - { - if (peer != null && !isLightweight()) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else - { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, - false, this)); - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false)); - } - } - } - else - pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED); - } - } + requestFocusImpl(false, true); } /** @@ -3880,61 +4276,7 @@ public abstract class Component */ protected boolean requestFocus (boolean temporary) { - if (isDisplayable () - && isShowing () - && isFocusable ()) - { - synchronized (getTreeLock ()) - { - // Find this Component's top-level ancestor. - Container parent = getParent (); - - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - Window toplevel = (Window) parent; - if (toplevel.isFocusableWindow ()) - { - if (peer != null && !isLightweight()) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else - { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, - FocusEvent.FOCUS_LOST, - temporary, this)); - eq.postEvent (new FocusEvent(this, - FocusEvent.FOCUS_GAINED, - temporary, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); - } - } - } - else - // FIXME: need to add a focus listener to our top-level - // ancestor, so that we can post this event when it becomes - // the focused window. - pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary); - } - } - // Always return true. - return true; + return requestFocusImpl(temporary, true); } /** @@ -3962,7 +4304,7 @@ public abstract class Component */ public boolean requestFocusInWindow () { - return requestFocusInWindow (false); + return requestFocusImpl(false, false); } /** @@ -3993,65 +4335,84 @@ public abstract class Component */ protected boolean requestFocusInWindow (boolean temporary) { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - - Window focusedWindow = manager.getFocusedWindow (); + return requestFocusImpl(temporary, false); + } - if (isDisplayable () - && isShowing () - && isFocusable ()) + /** + * Helper method for all 4 requestFocus variants. + * + * @param temporary indicates if the focus change is temporary + * @param focusWindow indicates if the window focus may be changed + * + * @return <code>false</code> if the request has been definitely denied, + * <code>true</code> otherwise + */ + private boolean requestFocusImpl(boolean temporary, boolean focusWindow) + { + boolean retval = false; + + // Don't try to focus non-focusable and non-visible components. + if (isFocusable() && isVisible()) { - if (focusedWindow != null) + ComponentPeer myPeer = peer; + if (peer != null) { - synchronized (getTreeLock ()) + // Find Window ancestor and find out if we're showing while + // doing this. + boolean showing = true; + Component window = this; + while (! (window instanceof Window)) { - Container parent = getParent (); - - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - Window toplevel = (Window) parent; - - // Check if top-level ancestor is currently focused window. - if (focusedWindow == toplevel) + if (! window.isVisible()) + showing = false; + window = window.parent; + } + // Don't allow focus when there is no window or the window + // is not focusable. + if (window != null && ((Window) window).isFocusableWindow() + && showing) + { + // Search for nearest heavy ancestor (including this + // component). + Component heavyweightParent = this; + while (heavyweightParent.peer instanceof LightweightPeer) + heavyweightParent = heavyweightParent.parent; + + // Don't allow focus on lightweight components without + // visible heavyweight ancestor + if (heavyweightParent != null && heavyweightParent.isVisible()) { - if (peer != null - && !isLightweight() - && !(this instanceof Window)) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else + // Don't allow focus when heavyweightParent has no peer. + myPeer = heavyweightParent.peer; + if (myPeer != null) { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) + // Register lightweight focus request. + if (heavyweightParent != this) + { + KeyboardFocusManager + .addLightweightFocusRequest(heavyweightParent, + this); + } + + // Try to focus the component. + long time = EventQueue.getMostRecentEventTime(); + boolean success = myPeer.requestFocus(this, temporary, + focusWindow, + time); + if (! success) { - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, - temporary, this)); - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + // Dequeue key events if focus request failed. + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.dequeueKeyEvents(time, this); } + retval = success; } } - else - return false; } } - - return true; } - return false; + return retval; } /** @@ -4552,7 +4913,7 @@ p * <li>the set of backward traversal keys Object newValue) { if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, oldValue, newValue); + changeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** @@ -4693,14 +5054,12 @@ p * <li>the set of backward traversal keys * {@link #applyComponentOrientation(ComponentOrientation)} affects the * entire hierarchy. * - * @param o the new orientation - * @throws NullPointerException if o is null + * @param o the new orientation (<code>null</code> is accepted) * @see #getComponentOrientation() */ public void setComponentOrientation(ComponentOrientation o) { - if (o == null) - throw new NullPointerException(); + ComponentOrientation oldOrientation = orientation; orientation = o; firePropertyChange("componentOrientation", oldOrientation, o); @@ -4709,7 +5068,7 @@ p * <li>the set of backward traversal keys /** * Determines the text layout orientation used by this component. * - * @return the component orientation + * @return the component orientation (this can be <code>null</code>) * @see #setComponentOrientation(ComponentOrientation) */ public ComponentOrientation getComponentOrientation() @@ -4864,7 +5223,7 @@ p * <li>the set of backward traversal keys if ((mods & InputEvent.ALT_DOWN_MASK) != 0) oldMods |= Event.ALT_MASK; - if (e instanceof MouseEvent) + if (e instanceof MouseEvent && !ignoreOldMouseEvents()) { if (id == MouseEvent.MOUSE_PRESSED) oldID = Event.MOUSE_DOWN; @@ -5045,52 +5404,26 @@ p * <li>the set of backward traversal keys void dispatchEventImpl(AWTEvent e) { - // This boolean tells us not to process focus events when the focus - // opposite component is the same as the focus component. - boolean ignoreFocus = - (e instanceof FocusEvent && - ((FocusEvent)e).getComponent() == ((FocusEvent)e).getOppositeComponent()); - - if (eventTypeEnabled (e.id)) + // Retarget focus events before dispatching it to the KeyboardFocusManager + // in order to handle lightweight components properly. + boolean dispatched = false; + if (! e.isFocusManagerEvent) { - if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE - && !ignoreFocus) - processEvent(e); - - // the trick we use to communicate between dispatch and redispatch - // is to have KeyboardFocusManager.redispatch synchronize on the - // object itself. we then do not redispatch to KeyboardFocusManager - // if we are already holding the lock. - if (! Thread.holdsLock(e)) + e = KeyboardFocusManager.retargetFocusEvent(e); + dispatched = KeyboardFocusManager.getCurrentKeyboardFocusManager() + .dispatchEvent(e); + } + + if (! dispatched) + { + if (eventTypeEnabled (e.id)) { - switch (e.id) - { - case WindowEvent.WINDOW_GAINED_FOCUS: - case WindowEvent.WINDOW_LOST_FOCUS: - case KeyEvent.KEY_PRESSED: - case KeyEvent.KEY_RELEASED: - case KeyEvent.KEY_TYPED: - case FocusEvent.FOCUS_GAINED: - case FocusEvent.FOCUS_LOST: - if (KeyboardFocusManager - .getCurrentKeyboardFocusManager() - .dispatchEvent(e)) - return; - case MouseEvent.MOUSE_PRESSED: - // A mouse click on an enabled lightweight component - // which has not yet been marked as consumed by any - // other mouse listener results in a focus traversal - // to that component. - if (isLightweight() - && isEnabled() && !e.isConsumed()) - requestFocus(); - break; - } + if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE) + processEvent(e); } + if (peer != null) + peer.handleEvent(e); } - - if (peer != null) - peer.handleEvent(e); } /** diff --git a/libjava/classpath/java/awt/Container.java b/libjava/classpath/java/awt/Container.java index 85a68ce13c2..409d164a13c 100644 --- a/libjava/classpath/java/awt/Container.java +++ b/libjava/classpath/java/awt/Container.java @@ -42,6 +42,7 @@ package java.awt; import java.awt.event.ComponentListener; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; +import java.awt.event.HierarchyEvent; import java.awt.event.KeyEvent; import java.awt.peer.ComponentPeer; import java.awt.peer.ContainerPeer; @@ -88,14 +89,16 @@ public class Container extends Component Dimension maxSize; /** - * Keeps track if the Container was cleared during a paint/update. + * @since 1.4 */ - private boolean backCleared; + boolean focusCycleRoot; /** - * @since 1.4 + * Indicates if this container provides a focus traversal policy. + * + * @since 1.5 */ - boolean focusCycleRoot; + private boolean focusTraversalPolicyProvider; int containerSerializedDataVersion; @@ -341,7 +344,7 @@ public class Container extends Component if (component == null) component = new Component[4]; // FIXME, better initial size? - + // This isn't the most efficient implementation. We could do less // copying when growing the array. It probably doesn't matter. if (ncomponents >= component.length) @@ -362,6 +365,16 @@ public class Container extends Component ++ncomponents; } + // Update the counter for Hierarchy(Bounds)Listeners. + int childHierarchyListeners = comp.numHierarchyListeners; + if (childHierarchyListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + childHierarchyListeners); + int childHierarchyBoundsListeners = comp.numHierarchyBoundsListeners; + if (childHierarchyBoundsListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + childHierarchyListeners); + // Notify the layout manager. if (layoutMgr != null) { @@ -388,6 +401,10 @@ public class Container extends Component ContainerListener[] listeners = getContainerListeners(); for (int i = 0; i < listeners.length; i++) listeners[i].componentAdded(ce); + + // Notify hierarchy listeners. + comp.fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, comp, + this, HierarchyEvent.PARENT_CHANGED); } } @@ -412,6 +429,16 @@ public class Container extends Component ncomponents - index - 1); component[--ncomponents] = null; + // Update the counter for Hierarchy(Bounds)Listeners. + int childHierarchyListeners = r.numHierarchyListeners; + if (childHierarchyListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -childHierarchyListeners); + int childHierarchyBoundsListeners = r.numHierarchyBoundsListeners; + if (childHierarchyBoundsListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -childHierarchyListeners); + invalidate(); if (layoutMgr != null) @@ -423,10 +450,14 @@ public class Container extends Component { // Post event to notify of removing the component. ContainerEvent ce = new ContainerEvent(this, - ContainerEvent.COMPONENT_REMOVED, - r); + ContainerEvent.COMPONENT_REMOVED, + r); getToolkit().getSystemEventQueue().postEvent(ce); } + + // Notify hierarchy listeners. + r.fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, r, + this, HierarchyEvent.PARENT_CHANGED); } } @@ -517,7 +548,8 @@ public class Container extends Component public void setLayout(LayoutManager mgr) { layoutMgr = mgr; - invalidate(); + if (valid) + invalidate(); } /** @@ -572,19 +604,22 @@ public class Container extends Component */ void invalidateTree() { - super.invalidate(); // Clean cached layout state. - for (int i = 0; i < ncomponents; i++) + synchronized (getTreeLock()) { - Component comp = component[i]; - comp.invalidate(); - if (comp instanceof Container) - ((Container) comp).invalidateTree(); - } + super.invalidate(); // Clean cached layout state. + for (int i = 0; i < ncomponents; i++) + { + Component comp = component[i]; + comp.invalidate(); + if (comp instanceof Container) + ((Container) comp).invalidateTree(); + } - if (layoutMgr != null && layoutMgr instanceof LayoutManager2) - { - LayoutManager2 lm2 = (LayoutManager2) layoutMgr; - lm2.invalidateLayout(this); + if (layoutMgr != null && layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + lm2.invalidateLayout(this); + } } } @@ -671,21 +706,25 @@ public class Container extends Component */ public Dimension preferredSize() { - synchronized(treeLock) - { - if(valid && prefSize != null) - return new Dimension(prefSize); - LayoutManager layout = getLayout(); - if (layout != null) + Dimension size = prefSize; + // Try to return cached value if possible. + if (size == null || !(prefSizeSet || valid)) + { + // Need to lock here. + synchronized (getTreeLock()) { - Dimension layoutSize = layout.preferredLayoutSize(this); - if(valid) - prefSize = layoutSize; - return new Dimension(layoutSize); + LayoutManager l = layoutMgr; + if (l != null) + prefSize = l.preferredLayoutSize(this); + else + prefSize = super.preferredSizeImpl(); + size = prefSize; } - else - return super.preferredSize (); } + if (size != null) + return new Dimension(size); + else + return size; } /** @@ -707,17 +746,25 @@ public class Container extends Component */ public Dimension minimumSize() { - if(valid && minSize != null) - return new Dimension(minSize); - - LayoutManager layout = getLayout(); - if (layout != null) + Dimension size = minSize; + // Try to return cached value if possible. + if (size == null || !(minSizeSet || valid)) { - minSize = layout.minimumLayoutSize (this); - return minSize; - } + // Need to lock here. + synchronized (getTreeLock()) + { + LayoutManager l = layoutMgr; + if (l != null) + minSize = l.minimumLayoutSize(this); + else + minSize = super.minimumSizeImpl(); + size = minSize; + } + } + if (size != null) + return new Dimension(size); else - return super.minimumSize (); + return size; } /** @@ -727,18 +774,25 @@ public class Container extends Component */ public Dimension getMaximumSize() { - if (valid && maxSize != null) - return new Dimension(maxSize); - - LayoutManager layout = getLayout(); - if (layout != null && layout instanceof LayoutManager2) + Dimension size = maxSize; + // Try to return cached value if possible. + if (size == null || !(maxSizeSet || valid)) { - LayoutManager2 lm2 = (LayoutManager2) layout; - maxSize = lm2.maximumLayoutSize(this); - return maxSize; + // Need to lock here. + synchronized (getTreeLock()) + { + LayoutManager l = layoutMgr; + if (l instanceof LayoutManager2) + maxSize = ((LayoutManager2) l).maximumLayoutSize(this); + else + maxSize = super.maximumSizeImpl(); + size = maxSize; + } } + if (size != null) + return new Dimension(size); else - return super.getMaximumSize(); + return size; } /** @@ -754,8 +808,11 @@ public class Container extends Component float alignmentX = 0.0F; if (layout != null && layout instanceof LayoutManager2) { - LayoutManager2 lm2 = (LayoutManager2) layout; - alignmentX = lm2.getLayoutAlignmentX(this); + synchronized (getTreeLock()) + { + LayoutManager2 lm2 = (LayoutManager2) layout; + alignmentX = lm2.getLayoutAlignmentX(this); + } } else alignmentX = super.getAlignmentX(); @@ -775,8 +832,11 @@ public class Container extends Component float alignmentY = 0.0F; if (layout != null && layout instanceof LayoutManager2) { - LayoutManager2 lm2 = (LayoutManager2) layout; - alignmentY = lm2.getLayoutAlignmentY(this); + synchronized (getTreeLock()) + { + LayoutManager2 lm2 = (LayoutManager2) layout; + alignmentY = lm2.getLayoutAlignmentY(this); + } } else alignmentY = super.getAlignmentY(); @@ -794,13 +854,10 @@ public class Container extends Component */ public void paint(Graphics g) { - if (!isShowing()) - return; - - // Visit heavyweights if the background was cleared - // for this container. - visitChildren(g, GfxPaintVisitor.INSTANCE, !backCleared); - backCleared = false; + if (isShowing()) + { + visitChildren(g, GfxPaintVisitor.INSTANCE, true); + } } /** @@ -830,14 +887,15 @@ public class Container extends Component // that overrides isLightweight() to return false, the background is // also not cleared. So we do a check on !(peer instanceof LightweightPeer) // instead. - ComponentPeer p = peer; - if (p != null && ! (p instanceof LightweightPeer)) + if (isShowing()) { - g.clearRect(0, 0, getWidth(), getHeight()); - backCleared = true; + ComponentPeer p = peer; + if (! (p instanceof LightweightPeer)) + { + g.clearRect(0, 0, getWidth(), getHeight()); + } + paint(g); } - - paint(g); } /** @@ -1173,8 +1231,11 @@ public class Container extends Component */ public void addNotify() { - super.addNotify(); - addNotifyContainerChildren(); + synchronized (getTreeLock()) + { + super.addNotify(); + addNotifyContainerChildren(); + } } /** @@ -1549,6 +1610,42 @@ public class Container extends Component } /** + * Set to <code>true</code> if this container provides a focus traversal + * policy, <code>false</code> when the root container's focus + * traversal policy should be used. + * + * @return <code>true</code> if this container provides a focus traversal + * policy, <code>false</code> when the root container's focus + * traversal policy should be used + * + * @see #setFocusTraversalPolicyProvider(boolean) + * + * @since 1.5 + */ + public final boolean isFocusTraversalPolicyProvider() + { + return focusTraversalPolicyProvider; + } + + /** + * Set to <code>true</code> if this container provides a focus traversal + * policy, <code>false</code> when the root container's focus + * traversal policy should be used. + * + * @param b <code>true</code> if this container provides a focus traversal + * policy, <code>false</code> when the root container's focus + * traversal policy should be used + * + * @see #isFocusTraversalPolicyProvider() + * + * @since 1.5 + */ + public final void setFocusTraversalPolicyProvider(boolean b) + { + focusTraversalPolicyProvider = b; + } + + /** * Check whether this Container is a focus cycle root. * * @return true if this is a focus cycle root, false otherwise @@ -1594,7 +1691,16 @@ public class Container extends Component public void applyComponentOrientation (ComponentOrientation orientation) { if (orientation == null) - throw new NullPointerException (); + throw new NullPointerException(); + + setComponentOrientation(orientation); + for (int i = 0; i < ncomponents; i++) + { + if (component[i] instanceof Container) + ((Container) component[i]).applyComponentOrientation(orientation); + else + component[i].setComponentOrientation(orientation); + } } public void addPropertyChangeListener (PropertyChangeListener listener) @@ -1646,24 +1752,27 @@ public class Container extends Component if (comp == this) throw new IllegalArgumentException("cannot add component to itself"); - // FIXME: Implement reparenting. - if ( comp.getParent() != this) - throw new AssertionError("Reparenting is not implemented yet"); - else + synchronized (getTreeLock()) { - // Find current component index. - int currentIndex = getComponentZOrder(comp); - if (currentIndex < index) - { - System.arraycopy(component, currentIndex + 1, component, - currentIndex, index - currentIndex); - } + // FIXME: Implement reparenting. + if ( comp.getParent() != this) + throw new AssertionError("Reparenting is not implemented yet"); else { - System.arraycopy(component, index, component, index + 1, - currentIndex - index); + // Find current component index. + int currentIndex = getComponentZOrder(comp); + if (currentIndex < index) + { + System.arraycopy(component, currentIndex + 1, component, + currentIndex, index - currentIndex); + } + else + { + System.arraycopy(component, index, component, index + 1, + currentIndex - index); + } + component[index] = comp; } - component[index] = comp; } } @@ -1682,19 +1791,22 @@ public class Container extends Component */ public final int getComponentZOrder(Component comp) { - int index = -1; - if (component != null) + synchronized (getTreeLock()) { - for (int i = 0; i < component.length; i++) + int index = -1; + if (component != null) { - if (component[i] == comp) + for (int i = 0; i < ncomponents; i++) { - index = i; - break; + if (component[i] == comp) + { + index = i; + break; + } } } + return index; } - return index; } // Hidden helper methods. @@ -1850,6 +1962,48 @@ public class Container extends Component } } + /** + * Fires hierarchy events to the children of this container and this + * container itself. This overrides {@link Component#fireHierarchyEvent} + * in order to forward this event to all children. + */ + void fireHierarchyEvent(int id, Component changed, Container parent, + long flags) + { + // Only propagate event if there is actually a listener waiting for it. + if ((id == HierarchyEvent.HIERARCHY_CHANGED && numHierarchyListeners > 0) + || ((id == HierarchyEvent.ANCESTOR_MOVED + || id == HierarchyEvent.ANCESTOR_RESIZED) + && numHierarchyBoundsListeners > 0)) + { + for (int i = 0; i < ncomponents; i++) + component[i].fireHierarchyEvent(id, changed, parent, flags); + super.fireHierarchyEvent(id, changed, parent, flags); + } + } + + /** + * Adjusts the number of hierarchy listeners of this container and all of + * its parents. This is called by the add/remove listener methods and + * structure changing methods in Container. + * + * @param type the type, either {@link AWTEvent#HIERARCHY_BOUNDS_EVENT_MASK} + * or {@link AWTEvent#HIERARCHY_EVENT_MASK} + * @param delta the number of listeners added or removed + */ + void updateHierarchyListenerCount(long type, int delta) + { + if (type == AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) + numHierarchyBoundsListeners += delta; + else if (type == AWTEvent.HIERARCHY_EVENT_MASK) + numHierarchyListeners += delta; + else + assert false : "Should not reach here"; + + if (parent != null) + parent.updateHierarchyListenerCount(type, delta); + } + private void addNotifyContainerChildren() { synchronized (getTreeLock ()) diff --git a/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java index 23b4ac2e8d3..14afd364876 100644 --- a/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java +++ b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java @@ -346,28 +346,30 @@ public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy || !root.isDisplayable ()) return null; - if (root.visible && root.isDisplayable() && root.enabled - && root.focusable) + if (accept(root)) return root; - Component[] componentArray = root.getComponents (); - - for (int i = 0; i < componentArray.length; i++) + int ncomponents = root.getComponentCount(); + for (int i = 0; i < ncomponents; i++) { - Component component = componentArray [i]; - - if (component.visible && component.isDisplayable() && component.enabled - && component.focusable) - return component; - - if (component instanceof Container) + Component component = root.getComponent(i); + if (component instanceof Container + && !((Container) component).isFocusCycleRoot()) { - Component result = getFirstComponent ((Container) component); - - if (result != null - && (result.visible && result.isDisplayable() && result.enabled && result.focusable)) - return result; + Component first = null; + Container cont = (Container) component; + if (cont.isFocusTraversalPolicyProvider()) + { + FocusTraversalPolicy childPol = cont.getFocusTraversalPolicy(); + first = childPol.getFirstComponent(cont); + } + else + first = getFirstComponent(cont); + if (first != null) + return first; } + else if (accept(component)) + return component; } return null; diff --git a/libjava/classpath/java/awt/Cursor.java b/libjava/classpath/java/awt/Cursor.java index 0ff987cd9ed..4d339b7211a 100644 --- a/libjava/classpath/java/awt/Cursor.java +++ b/libjava/classpath/java/awt/Cursor.java @@ -116,6 +116,16 @@ public class Cursor implements java.io.Serializable */ public static final int MOVE_CURSOR = 13; + private static String[] NAMES = { "Default Cursor", "Crosshair Cursor", + "Text Cursor", "Wait Cursor", + "Southwest Resize Cursor", + "Southeast Resize Cursor", + "Northwest Resize Cursor", + "Northeast Resize Cursor", + "North Resize Cursor", "South Resize Cursor", + "West Resize Cursor", "East Resize Cursor", + "Hand Cursor", "Move Cursor" }; + public static final int CUSTOM_CURSOR = 0xFFFFFFFF; private static final int PREDEFINED_COUNT = 14; @@ -142,7 +152,10 @@ public class Cursor implements java.io.Serializable throw new IllegalArgumentException ("invalid cursor " + type); this.type = type; - // FIXME: lookup and set name? + + name = NAMES[type]; + + // FIXME: lookup? } /** This constructor is used internally only. diff --git a/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java index 037cb834c40..9fea99b7839 100644 --- a/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java +++ b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java @@ -163,7 +163,13 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager if (e.id == WindowEvent.WINDOW_ACTIVATED) setGlobalActiveWindow (target); else if (e.id == WindowEvent.WINDOW_GAINED_FOCUS) - setGlobalFocusedWindow (target); + { + setGlobalFocusedWindow (target); + FocusTraversalPolicy p = target.getFocusTraversalPolicy(); + Component toFocus = p.getInitialComponent(target); + if (toFocus != null) + toFocus.requestFocusInWindow(); + } else if (e.id != WindowEvent.WINDOW_LOST_FOCUS && e.id != WindowEvent.WINDOW_DEACTIVATED) return false; @@ -173,51 +179,18 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager } else if (e instanceof FocusEvent) { - Component target = (Component) e.getSource (); + FocusEvent fe = (FocusEvent) e; + Component target = fe.getComponent (); + boolean retval = false; if (e.id == FocusEvent.FOCUS_GAINED) { - if (! (target instanceof Window)) - { - if (((FocusEvent) e).isTemporary ()) - setGlobalFocusOwner (target); - else - setGlobalPermanentFocusOwner (target); - } - - // Keep track of this window's focus owner. - - // Find the target Component's top-level ancestor. target - // may be a window. - Container parent = target.getParent (); - - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - // If the parent is null and target is not a window, then target is an - // unanchored component and so we don't want to set the focus owner. - if (! (parent == null && ! (target instanceof Window))) - { - Window toplevel = parent == null ? - (Window) target : (Window) parent; - - Component focusOwner = getFocusOwner (); - if (focusOwner != null - && ! (focusOwner instanceof Window)) - toplevel.setFocusOwner (focusOwner); - } + retval = handleFocusGained(fe); } else if (e.id == FocusEvent.FOCUS_LOST) { - if (((FocusEvent) e).isTemporary ()) - setGlobalFocusOwner (null); - else - setGlobalPermanentFocusOwner (null); + retval = handleFocusLost(fe); } - - redispatchEvent(target, e); - return true; } else if (e instanceof KeyEvent) @@ -256,6 +229,95 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager return false; } + /** + * Handles FOCUS_GAINED events in {@link #dispatchEvent(AWTEvent)}. + * + * @param fe the focus event + */ + private boolean handleFocusGained(FocusEvent fe) + { + Component target = fe.getComponent (); + + // If old focus owner != new focus owner, notify old focus + // owner that it has lost focus. + Component oldFocusOwner = getGlobalFocusOwner(); + if (oldFocusOwner != null && oldFocusOwner != target) + { + FocusEvent lost = new FocusEvent(oldFocusOwner, + FocusEvent.FOCUS_LOST, + fe.isTemporary(), target); + oldFocusOwner.dispatchEvent(lost); + } + + setGlobalFocusOwner (target); + if (target != getGlobalFocusOwner()) + { + // Focus transfer was rejected, like when the target is not + // focusable. + dequeueKeyEvents(-1, target); + // FIXME: Restore focus somehow. + } + else + { + if (! fe.isTemporary()) + { + setGlobalPermanentFocusOwner (target); + if (target != getGlobalPermanentFocusOwner()) + { + // Focus transfer was rejected, like when the target is not + // focusable. + dequeueKeyEvents(-1, target); + // FIXME: Restore focus somehow. + } + else + { + redispatchEvent(target, fe); + } + } + } + + return true; + } + + /** + * Handles FOCUS_LOST events for {@link #dispatchEvent(AWTEvent)}. + * + * @param fe the focus event + * + * @return if the event has been handled + */ + private boolean handleFocusLost(FocusEvent fe) + { + Component currentFocus = getGlobalFocusOwner(); + if (currentFocus != fe.getOppositeComponent()) + { + setGlobalFocusOwner(null); + if (getGlobalFocusOwner() != null) + { + // TODO: Is this possible? If so, then we should try to restore + // the focus. + } + else + { + if (! fe.isTemporary()) + { + setGlobalPermanentFocusOwner(null); + if (getGlobalPermanentFocusOwner() != null) + { + // TODO: Is this possible? If so, then we should try to + // restore the focus. + } + else + { + fe.setSource(currentFocus); + redispatchEvent(currentFocus, fe); + } + } + } + } + return true; + } + private boolean enqueueKeyEvent (KeyEvent e) { Iterator i = delayRequests.iterator (); diff --git a/libjava/classpath/java/awt/EventDispatchThread.java b/libjava/classpath/java/awt/EventDispatchThread.java index 7cb8af831bf..074a84975ac 100644 --- a/libjava/classpath/java/awt/EventDispatchThread.java +++ b/libjava/classpath/java/awt/EventDispatchThread.java @@ -82,17 +82,7 @@ class EventDispatchThread extends Thread try { AWTEvent evt = queue.getNextEvent(); - - KeyboardFocusManager manager; - manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - - // Try to dispatch this event to the current keyboard focus - // manager. It will dispatch all FocusEvents, all - // WindowEvents related to focus, and all KeyEvents, - // returning true. Otherwise, it returns false and we - // dispatch the event normally. - if (!manager.dispatchEvent (evt)) - queue.dispatchEvent(evt); + queue.dispatchEvent(evt); } catch (ThreadDeath death) { diff --git a/libjava/classpath/java/awt/FileDialog.java b/libjava/classpath/java/awt/FileDialog.java index 7f2723e7e9a..f02d06be2c9 100644 --- a/libjava/classpath/java/awt/FileDialog.java +++ b/libjava/classpath/java/awt/FileDialog.java @@ -101,6 +101,58 @@ private int mode; * Constructors */ + /** + * Initializes a new instance of <code>FileDialog</code> with the specified + * parent. This dialog will have no title and will be for loading a file. + * + * @param parent The parent dialog for this. + * + * @since 1.5 + */ + public FileDialog(Dialog parent) + { + this(parent, "", LOAD); + } + + /** + * Initialized a new instance of <code>FileDialog</code> with the + * specified parent and title. This dialog will be for opening a file. + * + * @param parent The parent dialog for this. + * @param title The title for this dialog. + * + * @since 1.5 + */ + public FileDialog(Dialog parent, String title) + { + this(parent, title, LOAD); + } + + /** + * Initialized a new instance of <code>FileDialog</code> with the specified + * parent, title, and mode. + * + * @param parent The parent dialog for this. + * @param title The title for this dialog. + * @param mode The mode of the dialog, either <code>LOAD</code> or + * <code>SAVE</code>. + * @throws IllegalArgumentException - if illegal mode, if + * GraphicsEnvironment.isHeadless or if parent is null. + * + * @since 1.5 + */ + public FileDialog(Dialog parent, String title, int mode) + { + super(parent, title, true); + + // Other IllegalArgumentException cases are taken care of in Window.java + if (mode != LOAD && mode != SAVE) + throw new IllegalArgumentException ( + "Mode argument must be either LOAD or SAVE"); + + setMode(mode); + } + /** * Initializes a new instance of <code>FileDialog</code> with the * specified parent. This dialog will have no title and will be for diff --git a/libjava/classpath/java/awt/FlowLayout.java b/libjava/classpath/java/awt/FlowLayout.java index 7d0771d915b..8c99195289a 100644 --- a/libjava/classpath/java/awt/FlowLayout.java +++ b/libjava/classpath/java/awt/FlowLayout.java @@ -276,26 +276,24 @@ public class FlowLayout implements LayoutManager, Serializable } /** - * Sets the horizontal gap between components to the specified value. - * + * Sets the horizontal gap between lines of components to the specified value. + * No Exception is thrown if hgap < 0. + * * @param hgap The new horizontal gap between components. */ public void setHgap (int hgap) { - if (hgap < 0) - throw new IllegalArgumentException ("horizontal gap must be nonnegative"); this.hgap = hgap; } /** * Sets the vertical gap between lines of components to the specified value. + * No Exception is thrown if vgap < 0. * * @param vgap The new vertical gap. */ public void setVgap (int vgap) { - if (vgap < 0) - throw new IllegalArgumentException ("vertical gap must be nonnegative"); this.vgap = vgap; } diff --git a/libjava/classpath/java/awt/Font.java b/libjava/classpath/java/awt/Font.java index a52f63408da..1c22ce7b48f 100644 --- a/libjava/classpath/java/awt/Font.java +++ b/libjava/classpath/java/awt/Font.java @@ -48,6 +48,8 @@ import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.peer.FontPeer; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; @@ -114,7 +116,14 @@ public class Font implements Serializable * @since 1.3 */ public static final int TRUETYPE_FONT = 0; - + + /** + * Indicates to <code>createFont</code> that the supplied font data + * is in Type1 format. + * + * @since 1.5 + */ + public static final int TYPE1_FONT = 1; /** * A flag for <code>layoutGlyphVector</code>, indicating that the @@ -576,6 +585,34 @@ public class Font implements Serializable } /** + * Creates a new font from a File object. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + * + * @param fontFormat - Integer code indicating the format the font data is + * in.Currently this can only be {@link #TRUETYPE_FONT}. + * @param file - a {@link File} from which font data will be read. + * + * @return A new {@link Font} of the format indicated. + * + * @throws IllegalArgumentException if <code>fontType</code> is not + * recognized. + * @throws NullPointerException if <code>file</code> is <code>null</code>. + * @throws FontFormatException if data in the file is invalid or cannot be read.. + * @throws SecurityException if the caller has no read permission for the file. + * @throws IOException if the file cannot be read + * + * @since 1.5 + */ + public static Font createFont (int fontFormat, File file) + throws FontFormatException, IOException + { + if( file == null ) + throw new NullPointerException("Null file argument"); + return tk().createFont(fontFormat, new FileInputStream( file )); + } + + /** * Maps characters to glyphs in a one-to-one relationship, returning a new * {@link GlyphVector} with a mapped glyph for each input character. This * sort of mapping is often sufficient for some scripts such as Roman, but diff --git a/libjava/classpath/java/awt/Graphics2D.java b/libjava/classpath/java/awt/Graphics2D.java index b3ecbc58a98..ada13edc512 100644 --- a/libjava/classpath/java/awt/Graphics2D.java +++ b/libjava/classpath/java/awt/Graphics2D.java @@ -53,23 +53,21 @@ import java.util.Map; /** * An abstract class defining a device independent two-dimensional vector * graphics API. Concrete subclasses implement this API for output of - * vector graphics to: (*) + * vector graphics to: * <p> * <ul> * <li>a {@link javax.swing.JComponent} - in the * {@link javax.swing.JComponent#paint(Graphics)} method, the incoming * {@link Graphics} should always be an instance of - * <code>Graphics2D</code> (*);</li> + * <code>Graphics2D</code>;</li> * <li>a {@link BufferedImage} - see - * {@link BufferedImage#createGraphics()} (*);</li> + * {@link BufferedImage#createGraphics()};</li> * <li>a {@link java.awt.print.PrinterJob} - in the * {@link Printable#print(Graphics, PageFormat, int)} method, the incoming - * {@link Graphics} should always be an instance of <code>Graphics2D</code> - * (*).</li> + * {@link Graphics} should always be an instance of + * <code>Graphics2D</code>.</li> * </ul> * <p> - * (*) Support for this API is not fully implemented in GNU Classpath yet. - * <p> * Third party libraries provide support for output to other formats via this * API, including encapsulated postscript (EPS), portable document format (PDF), * and scalable vector graphics (SVG). diff --git a/libjava/classpath/java/awt/GridBagConstraints.java b/libjava/classpath/java/awt/GridBagConstraints.java index 8d8b4fae534..a6a64c3bb8b 100644 --- a/libjava/classpath/java/awt/GridBagConstraints.java +++ b/libjava/classpath/java/awt/GridBagConstraints.java @@ -48,62 +48,99 @@ public class GridBagConstraints implements Cloneable, Serializable { static final long serialVersionUID = -1000070633030801713L; - /** Fill in both directions. */ - public static final int BOTH = 1; - /** Don't fill. */ + // Fill values. + /** + * Don't fill. + */ public static final int NONE = 0; - /** Fill horizontally. */ + + /** + * Fill in both directions. + */ + public static final int BOTH = 1; + + /** + * Fill horizontally. + */ public static final int HORIZONTAL = 2; - /** Fill vertically. */ + + /** + * Fill vertically. + */ public static final int VERTICAL = 3; - /** Position in the center. */ + // Anchor values. + /** + * Position in the center. + */ public static final int CENTER = 10; - /** Position to the east. */ - public static final int EAST = 13; - /** Position to the north. */ + + /** + * Position to the north. + */ public static final int NORTH = 11; - /** Position to the northeast. */ + + /** + * Position to the northeast. + */ public static final int NORTHEAST = 12; - /** Position to the northwest. */ - public static final int NORTHWEST = 18; - /** Position to the south. */ - public static final int SOUTH = 15; - /** Position to the southeast. */ + + /** + * Position to the east. + */ + public static final int EAST = 13; + + /** + * Position to the southeast. + */ public static final int SOUTHEAST = 14; - /** Position to the southwest. */ + + /** + * Position to the south. + */ + public static final int SOUTH = 15; + + /** + * Position to the southwest. + */ public static final int SOUTHWEST = 16; - /** Position to the west. */ + + /** + * Position to the west. + */ public static final int WEST = 17; - /** Occupy all remaining cells except last cell. */ + /** + * Position to the northwest. + */ + public static final int NORTHWEST = 18; + + // gridx and gridy values. + /** + * Occupy all remaining cells except last cell. + */ public static final int RELATIVE = -1; - /** Occupy all remaining cells. */ - public static final int REMAINDER = 0; /** - * Position to where the first text line would end. Equals to NORTHEAST for - * horizontal left-to-right orientations. + * Occupy all remaining cells. */ - public static final int FIRST_LINE_END = 24; + public static final int REMAINDER = 0; /** - * Position to where the first text line would start. Equals to NORTHWEST for - * horizontal left-to-right orientations. + * Position to where a page starts. Equals NORTH for horizontal orientations. */ - public static final int FIRST_LINE_START = 23; + public static final int PAGE_START = 19; /** - * Position to where the last text line would end. Equals to SOUTHEAST for - * horizontal left-to-right orientations. + * Position to where a page ends. Equals SOUTH for horizontal orientations. */ - public static final int LAST_LINE_END = 26; + public static final int PAGE_END = 20; /** - * Position to where the last text line would start. Equals to SOUTHWEST for - * horizontal left-to-right orientations. + * Position to where a text line would start. Equals to WEST for + * left-to-right orientations. */ - public static final int LAST_LINE_START = 25; + public static final int LINE_START = 21; /** * Position to where a text line would end. Equals to EAST for @@ -112,20 +149,28 @@ public class GridBagConstraints implements Cloneable, Serializable public static final int LINE_END = 22; /** - * Position to where a text line would start. Equals to WEST for - * left-to-right orientations. + * Position to where the first text line would start. Equals to NORTHWEST for + * horizontal left-to-right orientations. */ - public static final int LINE_START = 21; + public static final int FIRST_LINE_START = 23; /** - * Position to where a page ends. Equals SOUTH for horizontal orientations. + * Position to where the first text line would end. Equals to NORTHEAST for + * horizontal left-to-right orientations. */ - public static final int PAGE_END = 20; + public static final int FIRST_LINE_END = 24; /** - * Position to where a page starts. Equals NORTH for horizontal orientations. + * Position to where the last text line would start. Equals to SOUTHWEST for + * horizontal left-to-right orientations. */ - public static final int PAGE_START = 19; + public static final int LAST_LINE_START = 25; + + /** + * Position to where the last text line would end. Equals to SOUTHEAST for + * horizontal left-to-right orientations. + */ + public static final int LAST_LINE_END = 26; public int anchor; public int fill; @@ -139,7 +184,9 @@ public class GridBagConstraints implements Cloneable, Serializable public double weightx; public double weighty; - /** Create a copy of this object. */ + /** + * Create a copy of this object. + */ public Object clone () { try @@ -155,8 +202,10 @@ public class GridBagConstraints implements Cloneable, Serializable } } - /** Create a new GridBagConstraints object with the default - * parameters. */ + /** + * Create a new GridBagConstraints object with the default + * parameters. + */ public GridBagConstraints () { this.anchor = CENTER; @@ -172,8 +221,10 @@ public class GridBagConstraints implements Cloneable, Serializable this.weighty = 0; } - /** Create a new GridBagConstraints object with the indicated - * parameters. */ + /** + * Create a new GridBagConstraints object with the indicated + * parameters. + */ public GridBagConstraints (int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, diff --git a/libjava/classpath/java/awt/GridBagLayout.java b/libjava/classpath/java/awt/GridBagLayout.java index f827d21ca6a..d84b7d6df6c 100644 --- a/libjava/classpath/java/awt/GridBagLayout.java +++ b/libjava/classpath/java/awt/GridBagLayout.java @@ -1,5 +1,5 @@ /* GridBagLayout - Layout manager for components according to GridBagConstraints - Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,6 @@ exception statement from your version. */ package java.awt; -import gnu.classpath.NotImplementedException; - import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; @@ -322,13 +320,24 @@ public class GridBagLayout } /** - * Obsolete. + * Move and resize a rectangle according to a set of grid bag + * constraints. The x, y, width and height fields of the + * rectangle argument are adjusted to the new values. + * + * @param constraints position and size constraints + * @param r rectangle to be moved and resized */ - protected void AdjustForGravity (GridBagConstraints gbc, Rectangle rect) - throws NotImplementedException + protected void AdjustForGravity (GridBagConstraints constraints, + Rectangle r) { - // FIXME - throw new Error ("Not implemented"); + Insets insets = constraints.insets; + if (insets != null) + { + r.x += insets.left; + r.y += insets.top; + r.width -= insets.left + insets.right; + r.height -= insets.top + insets.bottom; + } } /** @@ -353,10 +362,9 @@ public class GridBagLayout // layoutInfo. So we wait until after this for loop to set // layoutInfo. Component lastComp = null; - int cellx = 0; - int celly = 0; - int cellw = 0; - int cellh = 0; + + Rectangle cell = new Rectangle(); + for (int i = 0; i < components.length; i++) { Component component = components[i]; @@ -370,29 +378,23 @@ public class GridBagLayout if (lastComp != null && constraints.gridheight == GridBagConstraints.REMAINDER) - celly += cellh; + cell.y += cell.height; else - celly = sumIntArray(info.rowHeights, constraints.gridy); + cell.y = sumIntArray(info.rowHeights, constraints.gridy); if (lastComp != null && constraints.gridwidth == GridBagConstraints.REMAINDER) - cellx += cellw; + cell.x += cell.width; else - cellx = sumIntArray(info.colWidths, constraints.gridx); + cell.x = sumIntArray(info.colWidths, constraints.gridx); - cellw = sumIntArray(info.colWidths, constraints.gridx - + constraints.gridwidth) - cellx; - cellh = sumIntArray(info.rowHeights, constraints.gridy - + constraints.gridheight) - celly; - - Insets insets = constraints.insets; - if (insets != null) - { - cellx += insets.left; - celly += insets.top; - cellw -= insets.left + insets.right; - cellh -= insets.top + insets.bottom; - } + cell.width = sumIntArray(info.colWidths, constraints.gridx + + constraints.gridwidth) - cell.x; + cell.height = sumIntArray(info.rowHeights, constraints.gridy + + constraints.gridheight) - cell.y; + + // Adjust for insets. + AdjustForGravity( constraints, cell ); // Note: Documentation says that padding is added on both sides, but // visual inspection shows that the Sun implementation only adds it @@ -403,14 +405,14 @@ public class GridBagLayout switch (constraints.fill) { case GridBagConstraints.HORIZONTAL: - dim.width = cellw; + dim.width = cell.width; break; case GridBagConstraints.VERTICAL: - dim.height = cellh; + dim.height = cell.height; break; case GridBagConstraints.BOTH: - dim.width = cellw; - dim.height = cellh; + dim.width = cell.width; + dim.height = cell.height; break; } @@ -420,40 +422,40 @@ public class GridBagLayout switch (constraints.anchor) { case GridBagConstraints.NORTH: - x = cellx + (cellw - dim.width) / 2; - y = celly; + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y; break; case GridBagConstraints.SOUTH: - x = cellx + (cellw - dim.width) / 2; - y = celly + cellh - dim.height; + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y + cell.height - dim.height; break; case GridBagConstraints.WEST: - x = cellx; - y = celly + (cellh - dim.height) / 2; + x = cell.x; + y = cell.y + (cell.height - dim.height) / 2; break; case GridBagConstraints.EAST: - x = cellx + cellw - dim.width; - y = celly + (cellh - dim.height) / 2; + x = cell.x + cell.width - dim.width; + y = cell.y + (cell.height - dim.height) / 2; break; case GridBagConstraints.NORTHEAST: - x = cellx + cellw - dim.width; - y = celly; + x = cell.x + cell.width - dim.width; + y = cell.y; break; case GridBagConstraints.NORTHWEST: - x = cellx; - y = celly; + x = cell.x; + y = cell.y; break; case GridBagConstraints.SOUTHEAST: - x = cellx + cellw - dim.width; - y = celly + cellh - dim.height; + x = cell.x + cell.width - dim.width; + y = cell.y + cell.height - dim.height; break; case GridBagConstraints.SOUTHWEST: - x = cellx; - y = celly + cellh - dim.height; + x = cell.x; + y = cell.y + cell.height - dim.height; break; default: - x = cellx + (cellw - dim.width) / 2; - y = celly + (cellh - dim.height) / 2; + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y + (cell.height - dim.height) / 2; break; } component.setBounds(info.pos_x + x, info.pos_y + y, dim.width, @@ -1082,10 +1084,18 @@ public class GridBagLayout } /** + * Move and resize a rectangle according to a set of grid bag + * constraints. The x, y, width and height fields of the + * rectangle argument are adjusted to the new values. + * + * @param constraints position and size constraints + * @param r rectangle to be moved and resized + * * @since 1.4 */ - protected void adjustForGravity (GridBagConstraints gbc, Rectangle rect) + protected void adjustForGravity (GridBagConstraints constraints, + Rectangle r) { - AdjustForGravity (gbc, rect); + AdjustForGravity (constraints, r); } } diff --git a/libjava/classpath/java/awt/GridLayout.java b/libjava/classpath/java/awt/GridLayout.java index 80d96414249..a6836681da5 100644 --- a/libjava/classpath/java/awt/GridLayout.java +++ b/libjava/classpath/java/awt/GridLayout.java @@ -254,14 +254,11 @@ public class GridLayout implements LayoutManager, Serializable this.cols = newCols; } - /** Set the horizontal gap + /** Set the horizontal gap. An Exception is not thrown if hgap < 0. * @param hgap The horizontal gap - * @exception IllegalArgumentException If the hgap value is less than zero. */ public void setHgap (int hgap) { - if (hgap < 0) - throw new IllegalArgumentException ("horizontal gap must be nonnegative"); this.hgap = hgap; } @@ -280,21 +277,18 @@ public class GridLayout implements LayoutManager, Serializable this.rows = newRows; } - /** Set the vertical gap. + /** Set the vertical gap. An Exception is not thrown if vgap < 0. * @param vgap The vertical gap - * @exception IllegalArgumentException If the vgap value is less than zero. */ public void setVgap (int vgap) { - if (vgap < 0) - throw new IllegalArgumentException ("vertical gap must be nonnegative"); this.vgap = vgap; } /** Return String description of this object. */ public String toString () { - return ("[" + getClass ().getName () + return (getClass ().getName () + "[" + ",hgap=" + hgap + ",vgap=" + vgap + ",rows=" + rows + ",cols=" + cols + "]"); diff --git a/libjava/classpath/java/awt/Image.java b/libjava/classpath/java/awt/Image.java index 6ade302a147..8a1cc0f0039 100644 --- a/libjava/classpath/java/awt/Image.java +++ b/libjava/classpath/java/awt/Image.java @@ -1,5 +1,5 @@ /* Image.java -- superclass for images - Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -50,7 +50,7 @@ import java.awt.image.ReplicateScaleFilter; * * @author Aaron M. Renn (arenn@urbanophile.com) * @since 1.0 - * @status updated to 1.4 + * @status updated to 1.5 */ public abstract class Image { @@ -102,6 +102,12 @@ public abstract class Image public static final int SCALE_AREA_AVERAGING = 16; /** + * The acceleration priority of the image + * @since 1.5 + */ + protected float accelerationPriority; + + /** * A default constructor for subclasses. */ public Image() @@ -205,4 +211,32 @@ public abstract class Image * includes the actual image data. */ public abstract void flush(); + + /** + * Sets the acceleration priority of the image. + * This is a value from 0 (lowest) to 1 (highest), which may + * be used as a hint for image acceleration. + * E.g. higher priority images may be stored in video memory. + * @param priority - the priority + * @throws IllegalArgumentException if priority is not >= 0 and <= 1. + * + * @since 1.5 + */ + public void setAccelerationPriority(float priority) + { + if( priority < 0f || priority > 1f) + throw new IllegalArgumentException("Invalid priority value."); + accelerationPriority = priority; + } + + /** + * Returns the acceleration priority of the image. + * + * @see #setAccelerationPriority(float) + * @since 1.5 + */ + public float getAccelerationPriority() + { + return accelerationPriority; + } } // class Image diff --git a/libjava/classpath/java/awt/Insets.java b/libjava/classpath/java/awt/Insets.java index 6d5bd122e9d..762b6975b3f 100644 --- a/libjava/classpath/java/awt/Insets.java +++ b/libjava/classpath/java/awt/Insets.java @@ -1,5 +1,5 @@ /* Insets.java -- information about a container border - Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -149,14 +149,13 @@ public class Insets implements Cloneable, Serializable /** * Returns a string representation of this object, which will be non-null. - * The format is unspecified, but appears to be <code>XXX what is it?</code>. * * @return a string representation of this object */ public String toString() { - return getClass().getName() + "(top=" + top + ",bottom=" + bottom + - ",left=" + left + ",right=" + right + ')'; + return getClass().getName() + "[top=" + top + ",left=" + left + + ",bottom=" + bottom + ",right=" + right + ']'; } /** diff --git a/libjava/classpath/java/awt/KeyboardFocusManager.java b/libjava/classpath/java/awt/KeyboardFocusManager.java index 371ea9bdf8a..eacbceb7d50 100644 --- a/libjava/classpath/java/awt/KeyboardFocusManager.java +++ b/libjava/classpath/java/awt/KeyboardFocusManager.java @@ -304,10 +304,7 @@ public abstract class KeyboardFocusManager */ public Component getFocusOwner () { - Component owner = (Component) getObject (currentFocusOwners); - if (owner == null) - owner = (Component) getObject (currentPermanentFocusOwners); - return owner; + return (Component) getObject (currentFocusOwners); } /** @@ -323,10 +320,7 @@ public abstract class KeyboardFocusManager */ protected Component getGlobalFocusOwner () { - // Check if there is a temporary focus owner. - Component focusOwner = (Component) getGlobalObject (currentFocusOwners); - - return (focusOwner == null) ? getGlobalPermanentFocusOwner () : focusOwner; + return (Component) getGlobalObject(currentFocusOwners, true); } /** @@ -409,7 +403,7 @@ public abstract class KeyboardFocusManager */ protected Component getGlobalPermanentFocusOwner () { - return (Component) getGlobalObject (currentPermanentFocusOwners); + return (Component) getGlobalObject (currentPermanentFocusOwners, true); } /** @@ -455,7 +449,7 @@ public abstract class KeyboardFocusManager */ protected Window getGlobalFocusedWindow () { - return (Window) getGlobalObject (currentFocusedWindows); + return (Window) getGlobalObject (currentFocusedWindows, true); } /** @@ -497,7 +491,7 @@ public abstract class KeyboardFocusManager */ protected Window getGlobalActiveWindow() { - return (Window) getGlobalObject (currentActiveWindows); + return (Window) getGlobalObject (currentActiveWindows, true); } /** @@ -663,7 +657,7 @@ public abstract class KeyboardFocusManager */ protected Container getGlobalCurrentFocusCycleRoot () { - return (Container) getGlobalObject (currentFocusCycleRoots); + return (Container) getGlobalObject (currentFocusCycleRoots, true); } /** @@ -1105,11 +1099,9 @@ public abstract class KeyboardFocusManager */ public final void redispatchEvent (Component target, AWTEvent e) { - synchronized (e) - { - e.setSource (target); - target.dispatchEvent (e); - } + e.isFocusManagerEvent = true; + target.dispatchEvent (e); + e.isFocusManagerEvent = false; } /** @@ -1355,17 +1347,19 @@ public abstract class KeyboardFocusManager * @see #getGlobalActiveWindow() * @see #getGlobalCurrentFocusCycleRoot() */ - private Object getGlobalObject (Map globalMap) + private Object getGlobalObject (Map globalMap, boolean checkThread) { - ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); - KeyboardFocusManager managerForCallingThread - = (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); - - if (this != managerForCallingThread) - throw new SecurityException ("Attempted to retrieve an object from a " - + "keyboard focus manager that isn't " - + "associated with the current thread group."); + if (checkThread) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager managerForCallingThread = + (KeyboardFocusManager) currentKeyboardFocusManagers.get(currentGroup); + if (this != managerForCallingThread) + throw new SecurityException ("Attempted to retrieve an object from a " + + "keyboard focus manager that isn't " + + "associated with the current thread group."); + } synchronized (globalMap) { Collection globalObjects = globalMap.values (); @@ -1406,7 +1400,7 @@ public abstract class KeyboardFocusManager synchronized (globalMap) { // Save old object. - Object oldObject = getGlobalObject (globalMap); + Object oldObject = getGlobalObject(globalMap, false); // Nullify old object. Collection threadGroups = globalMap.keySet (); @@ -1436,4 +1430,48 @@ public abstract class KeyboardFocusManager } } } + + + /** + * Maps focus requests from heavyweight to lightweight components. + */ + private static HashMap focusRequests = new HashMap(); + + /** + * Retargets focus events that come from the peer (which only know about + * heavyweight components) to go to the correct lightweight component + * if appropriate. + * + * @param ev the event to check + * + * @return the retargetted event + */ + static AWTEvent retargetFocusEvent(AWTEvent ev) + { + if (ev instanceof FocusEvent) + { + FocusEvent fe = (FocusEvent) ev; + Component target = fe.getComponent(); + if (focusRequests.containsKey(target)) + { + Component lightweight = (Component) focusRequests.get(target); + ev = new FocusEvent(lightweight, fe.id, fe.isTemporary()); + focusRequests.remove(target); + } + } + return ev; + } + + /** + * Adds a lightweight focus request for a heavyweight component. + * + * @param heavyweight the heavyweight from which we will receive a focus + * event soon + * @param lightweight the lightweight that ultimately receives the request + */ + static void addLightweightFocusRequest(Component heavyweight, + Component lightweight) + { + focusRequests.put(heavyweight, lightweight); + } } diff --git a/libjava/classpath/java/awt/Label.java b/libjava/classpath/java/awt/Label.java index d6db329106f..71614da6482 100644 --- a/libjava/classpath/java/awt/Label.java +++ b/libjava/classpath/java/awt/Label.java @@ -1,5 +1,6 @@ /* Label.java -- Java label widget - Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006, Free Software + Foundation, Inc. This file is part of GNU Classpath. @@ -45,275 +46,250 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; /** - * This component is used for displaying simple text strings that cannot - * be edited by the user. - * - * @author Aaron M. Renn (arenn@urbanophile.com) - * @author Tom Tromey (tromey@cygnus.com) - * @author Andrew John Hughes (gnu_andrew@member.fsf.org) - */ + * This component is used for displaying simple text strings that cannot + * be edited by the user. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ public class Label extends Component implements Accessible { -/* - * Static Variables - */ - -/** - * Alignment constant aligning the text to the left of its window. - */ -public static final int LEFT = 0; - -/** - * Alignment constant aligning the text in the center of its window. - */ -public static final int CENTER = 1; - -/** - * Alignment constant aligning the text to the right of its window. - */ -public static final int RIGHT = 2; - -// Serialization version constant: -private static final long serialVersionUID = 3094126758329070636L; - -/*************************************************************************/ + /** + * Alignment constant aligning the text to the left of its window. + */ + public static final int LEFT = 0; -/* - * Instance Variables - */ + /** + * Alignment constant aligning the text in the center of its window. + */ + public static final int CENTER = 1; -/** - * @serial Indicates the alignment of the text within this label's window. - * This is one of the constants in this class. The default value is - * <code>LEFT</code>. - */ -private int alignment; + /** + * Alignment constant aligning the text to the right of its window. + */ + public static final int RIGHT = 2; -/** - * @serial The text displayed in the label - */ -private String text; + // Serialization version constant: + private static final long serialVersionUID = 3094126758329070636L; -/*************************************************************************/ + /** + * @serial Indicates the alignment of the text within this label's window. + * This is one of the constants in this class. The default value is + * <code>LEFT</code>. + */ + private int alignment; -/* - * Constructors - */ + /** + * @serial The text displayed in the label + */ + private String text; -/** - * Initializes a new instance of <code>Label</code> with no text. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. - */ -public -Label() -{ - this("", LEFT); -} + /** + * Initializes a new instance of <code>Label</code> with no text. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label() + { + this("", LEFT); + } -/*************************************************************************/ + /** + * Initializes a new instance of <code>Label</code> with the specified + * text that is aligned to the left. + * + * @param text The text of the label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label(String text) + { + this(text, LEFT); + } -/** - * Initializes a new instance of <code>Label</code> with the specified - * text that is aligned to the left. - * - * @param text The text of the label. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. - */ -public -Label(String text) -{ - this(text, LEFT); -} + /** + * Initializes a new instance of <code>Label</code> with the specified + * text and alignment. + * + * @param text The text of the label. + * @param alignment The desired alignment for the text in this label, + * which must be one of <code>LEFT</code>, <code>CENTER</code>, or + * <code>RIGHT</code>. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label(String text, int alignment) + { + setAlignment(alignment); + setText(text); -/*************************************************************************/ + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } -/** - * Initializes a new instance of <code>Label</code> with the specified - * text and alignment. - * - * @param text The text of the label. - * @param alignment The desired alignment for the text in this label, - * which must be one of <code>LEFT</code>, <code>CENTER</code>, or - * <code>RIGHT</code>. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. - */ -public -Label(String text, int alignment) -{ - setAlignment (alignment); - setText (text); + /** + * Returns the constant indicating the alignment of the text in this + * label. The value returned will be one of the alignment constants + * from this class. + * + * @return The alignment of the text in the label. + */ + public int getAlignment() + { + return(alignment); + } - if (GraphicsEnvironment.isHeadless()) - throw new HeadlessException (); -} + /** + * Sets the text alignment of this label to the specified value. + * + * @param alignment The desired alignment for the text in this label, + * which must be one of <code>LEFT</code>, <code>CENTER</code>, or + * <code>RIGHT</code>. + */ + public synchronized void setAlignment(int alignment) + { + if (alignment != CENTER && alignment != LEFT && alignment != RIGHT) + throw new IllegalArgumentException("invalid alignment: " + alignment); + this.alignment = alignment; + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setAlignment(alignment); + } + } -/*************************************************************************/ + /** + * Returns the text displayed in this label. + * + * @return The text for this label. + */ + public String getText() + { + return text; + } -/* - * Instance Variables - */ + /** + * Sets the text in this label to the specified value. + * + * @param text The new text for this label. + */ + public synchronized void setText(String text) + { + if ((this.text == null && text != null) + || (this.text != null && ! this.text.equals(text))) + { + this.text = text; + + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setText(text); + } + invalidate(); + } + } -/** - * Returns the constant indicating the alignment of the text in this - * label. The value returned will be one of the alignment constants - * from this class. - * - * @return The alignment of the text in the label. - */ -public int -getAlignment() -{ - return(alignment); -} + /** + * Notifies this label that it has been added to a container, causing + * the peer to be created. This method is called internally by the AWT + * system. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createLabel(this); + super.addNotify(); + } -/*************************************************************************/ + /** + * Returns a parameter string useful for debugging. + * + * @return A debugging string. + */ + protected String paramString() + { + return ("text=" + getText() + ",alignment=" + + getAlignment() + "," + super.paramString()); + } -/** - * Sets the text alignment of this label to the specified value. - * - * @param alignment The desired alignment for the text in this label, - * which must be one of <code>LEFT</code>, <code>CENTER</code>, or - * <code>RIGHT</code>. - */ -public synchronized void -setAlignment(int alignment) -{ - if (alignment != CENTER && alignment != LEFT && alignment != RIGHT) - throw new IllegalArgumentException ("invalid alignment: " + alignment); - this.alignment = alignment; - if (peer != null) + /** + * This class provides accessibility support for the label. + */ + protected class AccessibleAWTLabel + extends AccessibleAWTComponent + { + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + private static final long serialVersionUID = -3568967560160480438L; + + /** + * Constructor for the accessible label. + */ + public AccessibleAWTLabel() { - LabelPeer lp = (LabelPeer) peer; - lp.setAlignment (alignment); } -} - -/*************************************************************************/ - -/** - * Returns the text displayed in this label. - * - * @return The text for this label. - */ -public String -getText() -{ - return(text); -} - -/*************************************************************************/ -/** - * Sets the text in this label to the specified value. - * - * @param text The new text for this label. - */ -public synchronized void -setText(String text) -{ - if ((this.text == null && text != null) - || (this.text != null && ! this.text.equals(text))) + /** + * Returns the accessible name for the label. This is + * the text used in the label. + * + * @return a <code>String</code> containing the accessible + * name for this label. + */ + public String getAccessibleName() { - this.text = text; - - if (peer != null) - { - LabelPeer lp = (LabelPeer) peer; - lp.setText (text); - } - invalidate(); + return getText(); } -} - -/*************************************************************************/ - -/** - * Notifies this label that it has been added to a container, causing - * the peer to be created. This method is called internally by the AWT - * system. - */ -public void -addNotify() -{ - if (peer == null) - peer = getToolkit ().createLabel (this); - super.addNotify (); -} -/*************************************************************************/ - -/** - * Returns a parameter string useful for debugging. - * - * @return A debugging string. - */ -protected String -paramString() -{ - return ("text=" + getText() + ",alignment=" + - getAlignment() + "," + super.paramString()); -} + /** + * Returns the accessible role for the label. + * + * @return an instance of <code>AccessibleRole</code>, describing + * the role of the label. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LABEL; + } -/** - * This class provides accessibility support for the label. - */ -protected class AccessibleAWTLabel - extends AccessibleAWTComponent -{ - /** - * For compatability with Sun's JDK 1.4.2 rev. 5 - */ - private static final long serialVersionUID = -3568967560160480438L; + } /** - * Constructor for the accessible label. + * Gets the AccessibleContext associated with this <code>Label</code>. + * The context is created, if necessary. + * + * @return the associated context */ - public AccessibleAWTLabel() + public AccessibleContext getAccessibleContext() { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTLabel(); + return accessibleContext; } /** - * Returns the accessible name for the label. This is - * the text used in the label. + * Generate a unique name for this button. * - * @return a <code>String</code> containing the accessible - * name for this label. + * @return A unique name for this button. */ - public String getAccessibleName() + String generateName() { - return getText(); + return "label" + getUniqueLong(); } - + /** - * Returns the accessible role for the label. - * - * @return an instance of <code>AccessibleRole</code>, describing - * the role of the label. + * The number used to generate the name returned by getName. */ - public AccessibleRole getAccessibleRole() + private static transient long nextLabelNumber; + + private static synchronized long getUniqueLong() { - return AccessibleRole.LABEL; + return nextLabelNumber++; } } -/** - * Gets the AccessibleContext associated with this <code>Label</code>. - * The context is created, if necessary. - * - * @return the associated context - */ -public AccessibleContext getAccessibleContext() -{ - /* Create the context if this is the first request */ - if (accessibleContext == null) - accessibleContext = new AccessibleAWTLabel(); - return accessibleContext; -} - -} // class Label - diff --git a/libjava/classpath/java/awt/LightweightDispatcher.java b/libjava/classpath/java/awt/LightweightDispatcher.java index 7e33bd4e9ce..3ea3f90a643 100644 --- a/libjava/classpath/java/awt/LightweightDispatcher.java +++ b/libjava/classpath/java/awt/LightweightDispatcher.java @@ -152,8 +152,11 @@ class LightweightDispatcher target = findTarget(parent, loc); while (target == null && parent != null) { - if (parent.getMouseListeners().length > 0 - || parent.getMouseMotionListeners().length > 0) + if (parent.mouseListener != null + || parent.mouseMotionListener != null + || (parent.eventMask + & (AWTEvent.MOUSE_EVENT_MASK + | AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0) { target = parent; } @@ -175,24 +178,22 @@ class LightweightDispatcher new MouseEvent(lastTarget, MouseEvent.MOUSE_EXITED, ev.getWhen(), ev.getModifiers(), p1.x, p1.y, ev.getClickCount(), ev.isPopupTrigger()); + //System.err.println("event: " + mouseExited); lastTarget.dispatchEvent(mouseExited); } - // If a target exists dispatch the MOUSE_ENTERED event only if - // there is currently no component from which a drag operation - // started (dragTarget == null) or the target is that component - // (dragTarget == target) - // That way a user can click and hold on a button (putting it into - // the armed state), move the cursor above other buttons without - // affecting their rollover state and get back to the initial - // button. - if (target != null && (dragTarget == null || dragTarget == target)) + // If a target exists dispatch the MOUSE_ENTERED event. + // Experimenting shows that the MOUSE_ENTERED is also dispatched + // when the mouse is dragging. + if (target != null) { Point p = convertPointToChild(window, ev.getPoint(), target); MouseEvent mouseEntered = - new MouseEvent(target, MouseEvent.MOUSE_ENTERED, ev.getWhen(), + new MouseEvent(target, + MouseEvent.MOUSE_ENTERED, ev.getWhen(), ev.getModifiers(), p.x, p.y, ev.getClickCount(), ev.isPopupTrigger()); + //System.err.println("event: " + mouseEntered); target.dispatchEvent(mouseEntered); } } @@ -219,7 +220,11 @@ class LightweightDispatcher // it was released. if (dragTarget != null && dragButton == ev.getButton()) { - target = dragTarget; + // Only post MOUSE_RELEASED to dragTarget (set in + // MOUSE_PRESSED) when the dragTarget is actually visible. + // Otherwise post the event to the normal target. + if (dragTarget.isVisible()) + target = dragTarget; dragTarget = null; } @@ -287,18 +292,21 @@ class LightweightDispatcher */ private Component findTarget(Container c, Point loc) { - Component[] children = c.getComponents(); + int numComponents = c.getComponentCount(); Component target = null; if (c != null) { - for (int i = 0; i < children.length; i++) + for (int i = 0; i < numComponents; i++) { - Component child = children[i]; + Component child = c.getComponent(i); if (child.isShowing()) { if (child.contains(loc.x - child.getX(), loc.y - child.getY()) - && (child.getMouseListeners().length > 0 - || child.getMouseMotionListeners().length > 0)) + && (child.mouseListener != null + || child.mouseMotionListener != null + || (child.eventMask + & (AWTEvent.MOUSE_EVENT_MASK + | AWTEvent.MOUSE_MOTION_EVENT_MASK)) != 0)) { target = child; break; diff --git a/libjava/classpath/java/awt/List.java b/libjava/classpath/java/awt/List.java index b28e2016d2e..86270234345 100644 --- a/libjava/classpath/java/awt/List.java +++ b/libjava/classpath/java/awt/List.java @@ -66,6 +66,11 @@ public class List extends Component * Static Variables */ +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_list_number; + // Serialization constant private static final long serialVersionUID = -3304312411574666869L; @@ -161,7 +166,11 @@ List(int rows) public List(int rows, boolean multipleMode) { - this.rows = rows; + if (rows == 0) + this.rows = 4; + else + this.rows = rows; + this.multipleMode = multipleMode; selected = new int[0]; @@ -645,13 +654,13 @@ clear() * @param item The new item value. * @param index The index of the item to replace. * - * @exception IllegalArgumentException If the index is not valid. + * @exception ArrayIndexOutOfBoundsException If the index is not valid. */ public synchronized void -replaceItem(String item, int index) throws IllegalArgumentException +replaceItem(String item, int index) throws ArrayIndexOutOfBoundsException { if ((index < 0) || (index >= items.size())) - throw new IllegalArgumentException("Bad list index: " + index); + throw new ArrayIndexOutOfBoundsException("Bad list index: " + index); items.insertElementAt(item, index + 1); items.removeElementAt (index); @@ -818,15 +827,11 @@ isSelected(int index) /** * This method ensures that the item at the specified index is visible. * - * @exception IllegalArgumentException If the specified index is out of - * range. + * @param index The index of the item to be made visible. */ public synchronized void makeVisible(int index) throws IllegalArgumentException { - if ((index < 0) || (index >= items.size())) - throw new IllegalArgumentException("Bad list index: " + index); - visibleIndex = index; if (peer != null) { @@ -1266,4 +1271,19 @@ paramString() accessibleContext = new AccessibleAWTList(); return accessibleContext; } + + /** + * Generate a unique name for this <code>List</code>. + * + * @return A unique name for this <code>List</code>. + */ + String generateName() + { + return "list" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_list_number++; + } } // class List diff --git a/libjava/classpath/java/awt/Menu.java b/libjava/classpath/java/awt/Menu.java index 6daec72cf44..f900d929574 100644 --- a/libjava/classpath/java/awt/Menu.java +++ b/libjava/classpath/java/awt/Menu.java @@ -58,6 +58,11 @@ public class Menu extends MenuItem implements MenuContainer, Serializable * Static Variables */ +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_menu_number; + // Serialization Constant private static final long serialVersionUID = -8809584163345499784L; @@ -485,5 +490,20 @@ paramString() accessibleContext = new AccessibleAWTMenu(); return accessibleContext; } + + /** + * Generate a unique name for this <code>Menu</code>. + * + * @return A unique name for this <code>Menu</code>. + */ + String generateName() + { + return "menu" + getUniqueLong(); + } + private static synchronized long getUniqueLong() + { + return next_menu_number++; + } + } // class Menu diff --git a/libjava/classpath/java/awt/MenuBar.java b/libjava/classpath/java/awt/MenuBar.java index 3c6b915649f..bd658cde6e3 100644 --- a/libjava/classpath/java/awt/MenuBar.java +++ b/libjava/classpath/java/awt/MenuBar.java @@ -60,10 +60,15 @@ public class MenuBar extends MenuComponent implements MenuContainer, Serializable, Accessible { -//Serialization Constant + // Serialization Constant private static final long serialVersionUID = -4930327919388951260L; /** + * The number used to generate the name returned by getName. + */ + private static transient long next_menubar_number; + + /** * @serial The menu used for providing help information */ private Menu helpMenu; @@ -331,6 +336,21 @@ public class MenuBar extends MenuComponent accessibleContext = new AccessibleAWTMenuBar(); return accessibleContext; } + + /** + * Generate a unique name for this <code>MenuBar</code>. + * + * @return A unique name for this <code>MenuBar</code>. + */ + String generateName() + { + return "menubar" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_menubar_number++; + } /** * This class provides accessibility support for AWT menu bars. diff --git a/libjava/classpath/java/awt/MenuComponent.java b/libjava/classpath/java/awt/MenuComponent.java index 9bb875069e0..163092685e7 100644 --- a/libjava/classpath/java/awt/MenuComponent.java +++ b/libjava/classpath/java/awt/MenuComponent.java @@ -200,8 +200,22 @@ public abstract class MenuComponent implements Serializable */ public String getName() { + if (name == null && ! nameExplicitlySet) + name = generateName(); return name; } + + /** + * Subclasses should override this to return unique component names like + * "menuitem0". + * + * @return the generated name for this menu component + */ + String generateName() + { + // MenuComponent is abstract. + return null; + } /** * Sets the name of this component to the specified name. diff --git a/libjava/classpath/java/awt/MenuItem.java b/libjava/classpath/java/awt/MenuItem.java index a7ac79643be..7cbc9219f54 100644 --- a/libjava/classpath/java/awt/MenuItem.java +++ b/libjava/classpath/java/awt/MenuItem.java @@ -63,9 +63,15 @@ public class MenuItem extends MenuComponent /* * Static Variables */ + + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_menuitem_number; -// Serialization Constant -private static final long serialVersionUID = -21757335363267194L; + // Serialization Constant + private static final long serialVersionUID = - 21757335363267194L; /*************************************************************************/ @@ -599,4 +605,19 @@ public AccessibleContext getAccessibleContext() return accessibleContext; } +/** + * Generate a unique name for this <code>MenuItem</code>. + * + * @return A unique name for this <code>MenuItem</code>. + */ +String generateName() +{ + return "menuitem" + getUniqueLong(); +} + +private static synchronized long getUniqueLong() +{ + return next_menuitem_number++; +} + } // class MenuItem diff --git a/libjava/classpath/java/awt/MouseInfo.java b/libjava/classpath/java/awt/MouseInfo.java new file mode 100644 index 00000000000..957b6bccbef --- /dev/null +++ b/libjava/classpath/java/awt/MouseInfo.java @@ -0,0 +1,95 @@ +/* MouseInfo.java -- utility methods for mice. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import java.awt.peer.MouseInfoPeer; + +/** + * MouseInfo is a class containing utility functions for mouse information. + * + * @author Sven de Marothy + * @since 1.5 + */ +public class MouseInfo +{ + private static MouseInfoPeer peer; + + /** + * Returns a PointerInfo object containing information about the current + * location of the mouse pointer + * + * @throws HeadlessException if the current GraphicsEnvironment is headless. + * @return a PointerInfo object. + */ + public static PointerInfo getPointerInfo() throws HeadlessException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission( new AWTPermission("watchMousePointer") ); + + if( GraphicsEnvironment.isHeadless() ) + throw new HeadlessException(); + + if( peer == null ) + peer = Toolkit.getDefaultToolkit().getMouseInfoPeer(); + + Point p = new Point(); + int screen = peer.fillPointWithCoords( p ); + + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getScreenDevices(); + + return new PointerInfo( gds[ screen ], p ); + } + + /** + * Returns the number of mouse buttons, or -1 if no mouse is connected. + * (mentioned in the 1.5 release notes) + * + * @throws HeadlessException if the current GraphicsEnvironment is headless. + * @return an integer number of buttons. + */ + public static int getNumberOfButtons() throws HeadlessException + { + if( GraphicsEnvironment.isHeadless() ) + throw new HeadlessException(); + return ((ClasspathToolkit)Toolkit.getDefaultToolkit()). + getMouseNumberOfButtons(); + } +} diff --git a/libjava/classpath/java/awt/Point.java b/libjava/classpath/java/awt/Point.java index 31b72e2cc75..64bc07eaf72 100644 --- a/libjava/classpath/java/awt/Point.java +++ b/libjava/classpath/java/awt/Point.java @@ -1,5 +1,5 @@ /* Point.java -- represents a point in 2-D space - Copyright (C) 1999, 2002 Free Software Foundation + Copyright (C) 1999, 2002, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -83,7 +83,7 @@ public class Point extends Point2D implements Serializable /** * Initializes a new instance of <code>Point</code> representing the - * coordiates (0,0). + * coordinates (0, 0). * * @since 1.1 */ @@ -93,7 +93,7 @@ public class Point extends Point2D implements Serializable /** * Initializes a new instance of <code>Point</code> with coordinates - * identical to the coordinates of the specified points. + * identical to the coordinates of the specified point. * * @param p the point to copy the coordinates from * @throws NullPointerException if p is null @@ -178,15 +178,16 @@ public class Point extends Point2D implements Serializable /** * Sets this object's coordinates to the specified values. This method - * performs normal casting from double to int, so you may lose precision. + * rounds to the nearest integer coordinates by adding 0.5 and calling + * {@link Math#floor(double)}. * * @param x the new X coordinate * @param y the new Y coordinate */ public void setLocation(double x, double y) { - this.x = (int) x; - this.y = (int) y; + this.x = (int) Math.floor(x + 0.5); + this.y = (int) Math.floor(y + 0.5); } /** diff --git a/libjava/classpath/java/awt/PointerInfo.java b/libjava/classpath/java/awt/PointerInfo.java new file mode 100644 index 00000000000..14d44a69b3f --- /dev/null +++ b/libjava/classpath/java/awt/PointerInfo.java @@ -0,0 +1,84 @@ +/* PointerInfo.java -- mouse pointer data + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * PointerInfo represents information about the mouse pointer, + * i.e. its GraphicsDevice and location. + * + * PointerInfo objects cannot be instantiated directly, but are + * retrieved from MouseInfo.getPointerInfo(). PointerInfo objects + * are immutable and will not be updated for future mouse motions. + * + * @since 1.5 + * @author Sven de Marothy + */ +public class PointerInfo +{ + private GraphicsDevice gd; + private Point p; + + /** + * Package-private constructor used by MouseInfo. + */ + PointerInfo( GraphicsDevice gd, Point p ) + { + this.gd = gd; + this.p = p; + } + + /** + * Returns the GraphicsDevice on which the mouse pointer was located + * + * @return a GraphicsDevice object. + */ + public GraphicsDevice getDevice() + { + return gd; + } + + /** + * Returns the coordinates of the mouse pointer. + * + * @return a Point object containing the pointer coordinates. + */ + public Point getLocation() + { + return p; + } +} diff --git a/libjava/classpath/java/awt/PopupMenu.java b/libjava/classpath/java/awt/PopupMenu.java index 540fffda718..9268678026d 100644 --- a/libjava/classpath/java/awt/PopupMenu.java +++ b/libjava/classpath/java/awt/PopupMenu.java @@ -55,8 +55,13 @@ public class PopupMenu extends Menu * Static Variables */ -// Serialization Constant -private static final long serialVersionUID = -4620452533522760060L; + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_popup_number; + + // Serialization Constant + private static final long serialVersionUID = - 4620452533522760060L; /*************************************************************************/ @@ -166,6 +171,21 @@ show(Component component, int x, int y) accessibleContext = new AccessibleAWTPopupMenu(); return accessibleContext; } + + /** + * Generate a unique name for this <code>PopupMenu</code>. + * + * @return A unique name for this <code>PopupMenu</code>. + */ + String generateName() + { + return "popup" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_popup_number++; + } } // class PopupMenu diff --git a/libjava/classpath/java/awt/ScrollPane.java b/libjava/classpath/java/awt/ScrollPane.java index 525d9d3e7da..65ce484b88d 100644 --- a/libjava/classpath/java/awt/ScrollPane.java +++ b/libjava/classpath/java/awt/ScrollPane.java @@ -46,6 +46,7 @@ import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; + /** * This widget provides a scrollable region that allows a single * subcomponent to be viewed through a smaller window. @@ -76,6 +77,11 @@ public static final int SCROLLBARS_ALWAYS = 1; */ public static final int SCROLLBARS_NEVER = 2; +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_scrollpane_number; + // Serialization constant private static final long serialVersionUID = 7956609840827222915L; @@ -221,7 +227,7 @@ getVAdjustable() * @return The viewport size. */ public Dimension getViewportSize () -{ +{ Dimension viewsize = getSize (); Insets insets = getInsets (); @@ -231,9 +237,9 @@ public Dimension getViewportSize () Component[] list = getComponents(); if ((list == null) || (list.length <= 0)) return viewsize; - + Dimension dim = list[0].getPreferredSize(); - + if (dim.width <= 0 && dim.height <= 0) return viewsize; @@ -276,7 +282,7 @@ public Dimension getViewportSize () needHorizontal = true; else if (dim.width > (viewsize.width - vScrollbarWidth)) mayNeedHorizontal = true; - + if (needVertical && mayNeedHorizontal) needHorizontal = true; @@ -288,7 +294,7 @@ public Dimension getViewportSize () if (needVertical) viewsize.width -= vScrollbarWidth; - + return viewsize; } @@ -613,5 +619,21 @@ paramString() accessibleContext = new AccessibleAWTScrollPane(); return accessibleContext; } + + /** + * Generate a unique name for this <code>ScrollPane</code>. + * + * @return A unique name for this <code>ScrollPane</code>. + */ + String generateName() + { + return "scrollpane" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_scrollpane_number++; + } + } // class ScrollPane diff --git a/libjava/classpath/java/awt/Shape.java b/libjava/classpath/java/awt/Shape.java index bd8a4343528..d76bbaba69d 100644 --- a/libjava/classpath/java/awt/Shape.java +++ b/libjava/classpath/java/awt/Shape.java @@ -1,5 +1,5 @@ /* Shape.java -- the classic Object-Oriented shape interface - Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -176,7 +176,8 @@ public interface Shape * not required, that the Shape isolate iterations from future changes to * the boundary, and document this fact. * - * @param transform an optional transform to apply to the iterator + * @param transform an optional transform to apply to the + * iterator (<code>null</code> permitted). * @return a new iterator over the boundary * @since 1.2 */ @@ -185,7 +186,7 @@ public interface Shape /** * Return an iterator along the flattened version of the shape boundary. * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE points are returned in the - * iterator. The flatness paramter controls how far points are allowed to + * iterator. The flatness parameter controls how far points are allowed to * differ from the real curve; although a limit on accuracy may cause this * parameter to be enlarged if needed. * @@ -194,10 +195,11 @@ public interface Shape * use. It is recommended, but not required, that the Shape isolate * iterations from future changes to the boundary, and document this fact. * - * @param transform an optional transform to apply to the iterator + * @param transform an optional transform to apply to the + * iterator (<code>null</code> permitted). * @param flatness the maximum distance for deviation from the real boundary * @return a new iterator over the boundary * @since 1.2 */ PathIterator getPathIterator(AffineTransform transform, double flatness); -} // interface Shape +} diff --git a/libjava/classpath/java/awt/TextArea.java b/libjava/classpath/java/awt/TextArea.java index b04cdc89204..7e3463ab849 100644 --- a/libjava/classpath/java/awt/TextArea.java +++ b/libjava/classpath/java/awt/TextArea.java @@ -125,9 +125,11 @@ public class TextArea extends TextComponent implements java.io.Serializable * the specified text. Conceptually the <code>TextArea</code> has 0 * rows and 0 columns but its initial bounds are defined by its peer * or by the container in which it is packed. Both horizontal and - * veritcal scrollbars will be displayed. + * veritcal scrollbars will be displayed. The TextArea initially contains + * the specified text. If text specified as <code>null<code>, it will + * be set to "". * - * @param text The text to display in this text area. + * @param text The text to display in this text area (<code>null</code> permitted). * * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true */ @@ -156,9 +158,10 @@ public class TextArea extends TextComponent implements java.io.Serializable * Initialize a new instance of <code>TextArea</code> that can * display the specified number of rows and columns of text, without * the need to scroll. The TextArea initially contains the - * specified text. + * specified text. If text specified as <code>null<code>, it will + * be set to "". * - * @param text The text to display in this text area. + * @param text The text to display in this text area (<code>null</code> permitted). * @param rows The number of rows in this text area. * @param columns The number of columns in this text area. * @@ -174,9 +177,10 @@ public class TextArea extends TextComponent implements java.io.Serializable * contains the specified text. The TextArea can display the * specified number of rows and columns of text, without the need to * scroll. This constructor allows specification of the scroll bar - * display policy. + * display policy. The TextArea initially contains the specified text. + * If text specified as <code>null<code>, it will be set to "". * - * @param text The text to display in this text area. + * @param text The text to display in this text area (<code>null</code> permitted). * @param rows The number of rows in this text area. * @param columns The number of columns in this text area. * @param scrollbarVisibility The scroll bar display policy. One of @@ -192,18 +196,20 @@ public class TextArea extends TextComponent implements java.io.Serializable if (GraphicsEnvironment.isHeadless ()) throw new HeadlessException (); - if (rows < 0 || columns < 0) - throw new IllegalArgumentException ("Bad row or column value"); - - if (scrollbarVisibility != SCROLLBARS_BOTH - && scrollbarVisibility != SCROLLBARS_VERTICAL_ONLY - && scrollbarVisibility != SCROLLBARS_HORIZONTAL_ONLY - && scrollbarVisibility != SCROLLBARS_NONE) - throw new IllegalArgumentException ("Bad scrollbar visibility value"); + if (rows < 0) + this.rows = 0; + else + this.rows = rows; + + if (columns < 0) + this.columns = 0; + else + this.columns = columns; - this.rows = rows; - this.columns = columns; - this.scrollbarVisibility = scrollbarVisibility; + if (scrollbarVisibility < 0 || scrollbarVisibility > 4) + this.scrollbarVisibility = SCROLLBARS_BOTH; + else + this.scrollbarVisibility = scrollbarVisibility; // TextAreas need to receive tab key events so we override the // default forward and backward traversal key sets. @@ -478,6 +484,8 @@ public class TextArea extends TextComponent implements java.io.Serializable if (peer != null) peer.insert (str, peer.getText().length ()); + else + setText(getText() + str); } /** @@ -504,10 +512,19 @@ public class TextArea extends TextComponent implements java.io.Serializable */ public void insertText (String str, int pos) { + String tmp1 = null; + String tmp2 = null; + TextAreaPeer peer = (TextAreaPeer) getPeer (); if (peer != null) peer.insert (str, pos); + else + { + tmp1 = getText().substring(0, pos); + tmp2 = getText().substring(pos, getText().length()); + setText(tmp1 + str + tmp2); + } } /** @@ -544,10 +561,19 @@ public class TextArea extends TextComponent implements java.io.Serializable */ public void replaceText (String str, int start, int end) { - TextAreaPeer peer = (TextAreaPeer) getPeer (); + String tmp1 = null; + String tmp2 = null; + + TextAreaPeer peer = (TextAreaPeer) getPeer(); if (peer != null) - peer.replaceRange (str, start, end); + peer.replaceRange(str, start, end); + else + { + tmp1 = getText().substring(0, start); + tmp2 = getText().substring(end, getText().length()); + setText(tmp1 + str + tmp2); + } } /** diff --git a/libjava/classpath/java/awt/TextComponent.java b/libjava/classpath/java/awt/TextComponent.java index f08e59c9fc9..f811122f2b2 100644 --- a/libjava/classpath/java/awt/TextComponent.java +++ b/libjava/classpath/java/awt/TextComponent.java @@ -1,5 +1,5 @@ /* TextComponent.java -- Widgets for entering text - Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -54,54 +54,45 @@ import javax.accessibility.AccessibleText; import javax.swing.text.AttributeSet; /** - * This class provides common functionality for widgets than - * contain text. - * - * @author Aaron M. Renn (arenn@urbanophile.com) - */ + * This class provides common functionality for widgets than + * contain text. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ public class TextComponent extends Component implements Serializable, Accessible { -/* - * Static Variables - */ - -// Constant for serialization -private static final long serialVersionUID = -2214773872412987419L; + private static final long serialVersionUID = -2214773872412987419L; -/* - * Instance Variables - */ - -/** - * @serial Indicates whether or not this component is editable. - * This is package-private to avoid an accessor method. - */ -boolean editable; + /** + * @serial Indicates whether or not this component is editable. + * This is package-private to avoid an accessor method. + */ + boolean editable; -/** - * @serial The starting position of the selected text region. - * This is package-private to avoid an accessor method. - */ -int selectionStart; + /** + * @serial The starting position of the selected text region. + * This is package-private to avoid an accessor method. + */ + int selectionStart; -/** - * @serial The ending position of the selected text region. - * This is package-private to avoid an accessor method. - */ -int selectionEnd; + /** + * @serial The ending position of the selected text region. + * This is package-private to avoid an accessor method. + */ + int selectionEnd; -/** - * @serial The text in the component - * This is package-private to avoid an accessor method. - */ -String text; + /** + * @serial The text in the component + * This is package-private to avoid an accessor method. + */ + String text; -/** - * A list of listeners that will receive events from this object. - */ -protected transient TextListener textListener; + /** + * A list of listeners that will receive events from this object. + */ + protected transient TextListener textListener; protected class AccessibleAWTTextComponent extends AccessibleAWTComponent @@ -318,360 +309,298 @@ protected transient TextListener textListener; } -/*************************************************************************/ - -/* - * Constructors - */ - -TextComponent(String text) -{ - this.text = text; - this.editable = true; -} - -/*************************************************************************/ -/* - * Instance Methods - */ - -/** - * Returns the text in this component - * - * @return The text in this component. - */ -public synchronized String -getText() -{ - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - text = tcp.getText(); + TextComponent(String text) + { + if (text == null) + this.text = ""; + else + this.text = text; + + this.editable = true; + } - return(text); -} -/*************************************************************************/ + /** + * Returns the text in this component + * + * @return The text in this component. + */ + public synchronized String getText() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + text = tcp.getText(); -/** - * Sets the text in this component to the specified string. - * - * @param text The new text for this component. - */ -public synchronized void -setText(String text) -{ - if (text == null) - text = ""; + return(text); + } - this.text = text; + /** + * Sets the text in this component to the specified string. + * + * @param text The new text for this component. + */ + public synchronized void setText(String text) + { + if (text == null) + text = ""; - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - tcp.setText(text); - setCaretPosition(0); -} + this.text = text; -/*************************************************************************/ + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setText(text); + setCaretPosition(0); + } -/** - * Returns a string that contains the text that is currently selected. - * - * @return The currently selected text region. - */ -public synchronized String -getSelectedText() -{ - String alltext = getText(); - int start = getSelectionStart(); - int end = getSelectionEnd(); + /** + * Returns a string that contains the text that is currently selected. + * + * @return The currently selected text region. + */ + public synchronized String getSelectedText() + { + String alltext = getText(); + int start = getSelectionStart(); + int end = getSelectionEnd(); - return(alltext.substring(start, end)); -} - -/*************************************************************************/ - -/** - * Returns the starting position of the selected text region. - * If the text is not selected then caret position is returned. - * - * @return The starting position of the selected text region. - */ -public synchronized int -getSelectionStart() -{ - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - selectionStart = tcp.getSelectionStart(); - - return(selectionStart); -} - -/*************************************************************************/ - -/** - * Sets the starting position of the selected region to the - * specified value. If the specified value is out of range, then it - * will be silently changed to the nearest legal value. - * - * @param selectionStart The new start position for selected text. - */ -public synchronized void -setSelectionStart(int selectionStart) -{ - select(selectionStart, getSelectionEnd()); -} - -/*************************************************************************/ - -/** - * Returns the ending position of the selected text region. - * If the text is not selected, then caret position is returned - * - * @return The ending position of the selected text region. - */ -public synchronized int -getSelectionEnd() -{ - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - selectionEnd = tcp.getSelectionEnd(); - - return(selectionEnd); -} + return(alltext.substring(start, end)); + } -/*************************************************************************/ + /** + * Returns the starting position of the selected text region. + * If the text is not selected then caret position is returned. + * + * @return The starting position of the selected text region. + */ + public synchronized int getSelectionStart() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + selectionStart = tcp.getSelectionStart(); -/** - * Sets the ending position of the selected region to the - * specified value. If the specified value is out of range, then it - * will be silently changed to the nearest legal value. - * - * @param selectionEnd The new start position for selected text. - */ -public synchronized void -setSelectionEnd(int selectionEnd) -{ - select(getSelectionStart(), selectionEnd); -} + return(selectionStart); + } -/*************************************************************************/ + /** + * Sets the starting position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionStart The new start position for selected text. + */ + public synchronized void setSelectionStart(int selectionStart) + { + select(selectionStart, getSelectionEnd()); + } -/** - * This method sets the selected text range to the text between the - * specified start and end positions. Illegal values for these - * positions are silently fixed. - * - * @param selectionStart The new start position for the selected text. - * @param selectionEnd The new end position for the selected text. - */ -public synchronized void -select(int selectionStart, int selectionEnd) -{ - if (selectionStart < 0) - selectionStart = 0; + /** + * Returns the ending position of the selected text region. + * If the text is not selected, then caret position is returned + * + * @return The ending position of the selected text region. + */ + public synchronized int getSelectionEnd() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + selectionEnd = tcp.getSelectionEnd(); - if (selectionStart > getText().length()) - selectionStart = text.length(); + return(selectionEnd); + } - if (selectionEnd > text.length()) - selectionEnd = text.length(); + /** + * Sets the ending position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionEnd The new start position for selected text. + */ + public synchronized void setSelectionEnd(int selectionEnd) + { + select(getSelectionStart(), selectionEnd); + } - if (selectionStart > selectionEnd) - selectionStart = selectionEnd; + /** + * This method sets the selected text range to the text between the + * specified start and end positions. Illegal values for these + * positions are silently fixed. + * + * @param selectionStart The new start position for the selected text. + * @param selectionEnd The new end position for the selected text. + */ + public synchronized void select(int selectionStart, int selectionEnd) + { + if (selectionStart < 0) + selectionStart = 0; - this.selectionStart = selectionStart; - this.selectionEnd = selectionEnd; + if (selectionStart > getText().length()) + selectionStart = text.length(); - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - tcp.select(selectionStart, selectionEnd); -} + if (selectionEnd > text.length()) + selectionEnd = text.length(); -/*************************************************************************/ + if (selectionStart > selectionEnd) + selectionStart = selectionEnd; -/** - * Selects all of the text in the component. - */ -public synchronized void -selectAll() -{ - select(0, getText().length()); -} - -/*************************************************************************/ + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.select(selectionStart, selectionEnd); + } -/** - * Returns the current caret position in the text. - * - * @return The caret position in the text. - */ -public synchronized int -getCaretPosition() -{ - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - return(tcp.getCaretPosition()); - else - return(0); -} + /** + * Selects all of the text in the component. + */ + public synchronized void selectAll() + { + select(0, getText().length()); + } -/*************************************************************************/ + /** + * Returns the current caret position in the text. + * + * @return The caret position in the text. + */ + public synchronized int getCaretPosition() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + return(tcp.getCaretPosition()); + else + return(0); + } -/** - * Sets the caret position to the specified value. - * - * @param caretPosition The new caret position. - * - * @exception IllegalArgumentException If the value supplied for position - * is less than zero. - * - * @since 1.1 - */ -public synchronized void -setCaretPosition(int caretPosition) -{ - if (caretPosition < 0) - throw new IllegalArgumentException (); + /** + * Sets the caret position to the specified value. + * + * @param caretPosition The new caret position. + * + * @exception IllegalArgumentException If the value supplied for position + * is less than zero. + * + * @since 1.1 + */ + public synchronized void setCaretPosition(int caretPosition) + { + if (caretPosition < 0) + throw new IllegalArgumentException(); - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - tcp.setCaretPosition(caretPosition); -} - -/*************************************************************************/ - -/** - * Tests whether or not this component's text can be edited. - * - * @return <code>true</code> if the text can be edited, <code>false</code> - * otherwise. - */ -public boolean -isEditable() -{ - return(editable); -} - -/*************************************************************************/ - -/** - * Sets whether or not this component's text can be edited. - * - * @param editable <code>true</code> to enable editing of the text, - * <code>false</code> to disable it. - */ -public synchronized void -setEditable(boolean editable) -{ - this.editable = editable; - - TextComponentPeer tcp = (TextComponentPeer)getPeer(); - if (tcp != null) - tcp.setEditable(editable); -} - -/*************************************************************************/ - -/** - * Notifies the component that it should destroy its native peer. - */ -public void -removeNotify() -{ - super.removeNotify(); -} - -/*************************************************************************/ + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setCaretPosition(caretPosition); + } -/** - * Adds a new listener to the list of text listeners for this - * component. - * - * @param listener The listener to be added. - */ -public synchronized void -addTextListener(TextListener listener) -{ - textListener = AWTEventMulticaster.add(textListener, listener); + /** + * Tests whether or not this component's text can be edited. + * + * @return <code>true</code> if the text can be edited, <code>false</code> + * otherwise. + */ + public boolean isEditable() + { + return(editable); + } - enableEvents(AWTEvent.TEXT_EVENT_MASK); -} + /** + * Sets whether or not this component's text can be edited. + * + * @param editable <code>true</code> to enable editing of the text, + * <code>false</code> to disable it. + */ + public synchronized void setEditable(boolean editable) + { + this.editable = editable; -/*************************************************************************/ + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setEditable(editable); + } -/** - * Removes the specified listener from the list of listeners - * for this component. - * - * @param listener The listener to remove. - */ -public synchronized void -removeTextListener(TextListener listener) -{ - textListener = AWTEventMulticaster.remove(textListener, listener); -} + /** + * Notifies the component that it should destroy its native peer. + */ + public void removeNotify() + { + super.removeNotify(); + } -/*************************************************************************/ + /** + * Adds a new listener to the list of text listeners for this + * component. + * + * @param listener The listener to be added. + */ + public synchronized void addTextListener(TextListener listener) + { + textListener = AWTEventMulticaster.add(textListener, listener); -/** - * Processes the specified event for this component. Text events are - * processed by calling the <code>processTextEvent()</code> method. - * All other events are passed to the superclass method. - * - * @param event The event to process. - */ -protected void -processEvent(AWTEvent event) -{ - if (event instanceof TextEvent) - processTextEvent((TextEvent)event); - else - super.processEvent(event); -} + enableEvents(AWTEvent.TEXT_EVENT_MASK); + } -/*************************************************************************/ + /** + * Removes the specified listener from the list of listeners + * for this component. + * + * @param listener The listener to remove. + */ + public synchronized void removeTextListener(TextListener listener) + { + textListener = AWTEventMulticaster.remove(textListener, listener); + } -/** - * Processes the specified text event by dispatching it to any listeners - * that are registered. Note that this method will only be called - * if text event's are enabled. This will be true if there are any - * registered listeners, or if the event has been specifically - * enabled using <code>enableEvents()</code>. - * - * @param event The text event to process. - */ -protected void -processTextEvent(TextEvent event) -{ - if (textListener != null) - textListener.textValueChanged(event); -} + /** + * Processes the specified event for this component. Text events are + * processed by calling the <code>processTextEvent()</code> method. + * All other events are passed to the superclass method. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof TextEvent) + processTextEvent((TextEvent)event); + else + super.processEvent(event); + } -void -dispatchEventImpl(AWTEvent e) -{ - if (e.id <= TextEvent.TEXT_LAST - && e.id >= TextEvent.TEXT_FIRST - && (textListener != null - || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) - processEvent(e); - else - super.dispatchEventImpl(e); -} + /** + * Processes the specified text event by dispatching it to any listeners + * that are registered. Note that this method will only be called + * if text event's are enabled. This will be true if there are any + * registered listeners, or if the event has been specifically + * enabled using <code>enableEvents()</code>. + * + * @param event The text event to process. + */ + protected void processTextEvent(TextEvent event) + { + if (textListener != null) + textListener.textValueChanged(event); + } -/*************************************************************************/ + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= TextEvent.TEXT_LAST + && e.id >= TextEvent.TEXT_FIRST + && (textListener != null + || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } -/** - * Returns a debugging string. - * - * @return A debugging string. - */ -protected String -paramString() -{ - return(getClass().getName() + "(text=" + getText() + ")"); -} + /** + * Returns a debugging string. + * + * @return A debugging string. + */ + protected String paramString() + { + return(getClass().getName() + "(text=" + getText() + ")"); + } /** * Returns an array of all the objects currently registered as FooListeners @@ -681,20 +610,20 @@ paramString() * @exception ClassCastException If listenerType doesn't specify a class or * interface that implements java.util.EventListener. */ - public EventListener[] getListeners (Class listenerType) + public EventListener[] getListeners(Class listenerType) { if (listenerType == TextListener.class) - return AWTEventMulticaster.getListeners (textListener, listenerType); + return AWTEventMulticaster.getListeners(textListener, listenerType); - return super.getListeners (listenerType); + return super.getListeners(listenerType); } /** * Returns all text listeners registered to this object. */ - public TextListener[] getTextListeners () + public TextListener[] getTextListeners() { - return (TextListener[]) getListeners (TextListener.class); + return (TextListener[]) getListeners(TextListener.class); } /** @@ -712,30 +641,35 @@ paramString() } - /*******************************/ // Provide AccessibleAWTTextComponent access to several peer functions that // aren't publicly exposed. This is package-private to avoid an accessor // method. - synchronized int - getIndexAtPoint(Point p) + synchronized int getIndexAtPoint(Point p) { - TextComponentPeer tcp = (TextComponentPeer)getPeer(); + TextComponentPeer tcp = (TextComponentPeer) getPeer(); if (tcp != null) return tcp.getIndexAtPoint(p.x, p.y); return -1; } - synchronized Rectangle - getCharacterBounds(int i) + synchronized Rectangle getCharacterBounds(int i) { - TextComponentPeer tcp = (TextComponentPeer)getPeer(); + TextComponentPeer tcp = (TextComponentPeer) getPeer(); if (tcp != null) return tcp.getCharacterBounds(i); return null; } - - + /** + * All old mouse events for this component should + * be ignored. + * + * @return true to ignore all old mouse events. + */ + static boolean ignoreOldMouseEvents() + { + return true; + } } // class TextComponent diff --git a/libjava/classpath/java/awt/TextField.java b/libjava/classpath/java/awt/TextField.java index 23d3d918ff4..b76f393a0b6 100644 --- a/libjava/classpath/java/awt/TextField.java +++ b/libjava/classpath/java/awt/TextField.java @@ -1,5 +1,5 @@ /* TextField.java -- A one line text entry field - Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -48,450 +48,369 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleStateSet; /** - * This class implements a single line text entry field widget - * - * @author Aaron M. Renn (arenn@urbanophile.com) - */ -public class TextField extends TextComponent -{ - -/* - * Static Variables - */ - -// Serialization constant -private static final long serialVersionUID = -2966288784432217853L; - -/*************************************************************************/ - -/* - * Instance Variables - */ - -/** - * @serial The number of columns in the text entry field. - */ -private int columns; - -/** - * @serial The character that is echoed when doing protected input - */ -private char echoChar; - -// List of registered ActionListener's for this object. -private ActionListener action_listeners; - -/*************************************************************************/ - -/* - * Constructors - */ - -/** - * Initializes a new instance of <code>TextField</code> that is empty - * and has one column. + * This class implements a single line text entry field widget * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, - */ -public -TextField() -{ - this("", 1); -} - -/*************************************************************************/ - -/** - * Initializes a new instance of <code>TextField</code> containing - * the specified text. The number of columns will be equal to the - * length of the text string. - * - * @param text The text to display in the field. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, - */ -public -TextField(String text) -{ - this(text, text.length()); -} - -/*************************************************************************/ - -/** - * Initializes a new instance of <code>TextField</code> that is empty - * and has the specified number of columns. - * - * @param columns The number of columns in the text field. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, - */ -public -TextField(int columns) -{ - this("", columns); -} - -/*************************************************************************/ - -/** - * Initializes a new instance of <code>TextField</code> with the - * specified text and number of columns. - * - * @param text The text to display in the field. - * @param columns The number of columns in the field. - * - * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, - */ -public -TextField(String text, int columns) -{ - super(text); - this.columns = columns; - - if (GraphicsEnvironment.isHeadless()) - throw new HeadlessException (); -} - -/*************************************************************************/ - -/* - * Instance Methods + * @author Aaron M. Renn (arenn@urbanophile.com) */ - -/** - * Returns the number of columns in the field. - * - * @return The number of columns in the field. - */ -public int -getColumns() -{ - return(columns); -} - -/*************************************************************************/ - -/** - * Sets the number of columns in this field to the specified value. - * - * @param columns The new number of columns in the field. - * - * @exception IllegalArgumentException If columns is less than zero. - */ -public synchronized void -setColumns(int columns) -{ - if (columns < 0) - throw new IllegalArgumentException("Value is less than zero: " + - columns); - - this.columns = columns; - // FIXME: How to we communicate this to our peer? -} - -/*************************************************************************/ - -/** - * Returns the character that is echoed to the screen when a text - * field is protected (such as when a password is being entered). - * - * @return The echo character for this text field. - */ -public char -getEchoChar() -{ - return(echoChar); -} - -/*************************************************************************/ - -/** - * Sets the character that is echoed when protected input such as - * a password is displayed. - * - * @param echoChar The new echo character. - */ -public void -setEchoChar(char echoChar) +public class TextField extends TextComponent { - setEchoCharacter (echoChar); -} - -/*************************************************************************/ + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_textfield_number; -/** - * Sets the character that is echoed when protected input such as - * a password is displayed. - * - * @param echoChar The new echo character. - * - * @deprecated This method is deprecated in favor of - * <code>setEchoChar()</code> - */ -public void -setEchoCharacter(char echoChar) -{ - this.echoChar = echoChar; + + private static final long serialVersionUID = -2966288784432217853L; - TextFieldPeer peer = (TextFieldPeer) getPeer (); - if (peer != null) - peer.setEchoChar (echoChar); -} -/*************************************************************************/ + /** + * @serial The number of columns in the text entry field. + */ + private int columns; -/** - * Tests whether or not this text field has an echo character set - * so that characters the user type are not echoed to the screen. - * - * @return <code>true</code> if an echo character is set, - * <code>false</code> otherwise. - */ -public boolean -echoCharIsSet() -{ - if (echoChar == '\u0000') - return(false); - else - return(true); -} + /** + * @serial The character that is echoed when doing protected input + */ + private char echoChar; -/*************************************************************************/ + // List of registered ActionListener's for this object. + private ActionListener action_listeners; -/** - * Returns the minimum size for this text field. - * - * @return The minimum size for this text field. - */ -public Dimension -getMinimumSize() -{ - return getMinimumSize (getColumns ()); -} + /** + * Initializes a new instance of <code>TextField</code> that is empty + * and has one column. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField() + { + this("", 0); + } -/*************************************************************************/ + /** + * Initializes a new instance of <code>TextField</code> containing + * the specified text. The number of columns will be equal to the + * length of the text string. + * + * @param text The text to display in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(String text) + { + this(text, (text == null) ? 0 : text.length()); + } -/** - * Returns the minimum size of a text field with the specified number - * of columns. - * - * @param columns The number of columns to get the minimum size for. - */ -public Dimension -getMinimumSize(int columns) -{ - return minimumSize (columns); -} + /** + * Initializes a new instance of <code>TextField</code> that is empty + * and has the specified number of columns. + * + * @param columns The number of columns in the text field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(int columns) + { + this("", columns); + } -/*************************************************************************/ + /** + * Initializes a new instance of <code>TextField</code> with the + * specified text and number of columns. + * + * @param text The text to display in the field. + * @param columns The number of columns in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(String text, int columns) + { + super(text); + + if (columns < 0) + this.columns = 0; + else + this.columns = columns; -/** - * Returns the minimum size for this text field. - * - * @return The minimum size for this text field. - * - * @deprecated This method is deprecated in favor of - * <code>getMinimumSize()</code>. - */ -public Dimension -minimumSize() -{ - return minimumSize (getColumns ()); -} + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + } -/*************************************************************************/ + /** + * Returns the number of columns in the field. + * + * @return The number of columns in the field. + */ + public int getColumns() + { + return(columns); + } -/** - * Returns the minimum size of a text field with the specified number - * of columns. - * - * @param columns The number of columns to get the minimum size for. - * - * @deprecated This method is deprecated in favor of - * <code>getMinimumSize(int)</code>. - */ -public Dimension -minimumSize(int columns) -{ - TextFieldPeer peer = (TextFieldPeer) getPeer (); - if (peer == null) - return null; // FIXME: What do we do if there is no peer? + /** + * Sets the number of columns in this field to the specified value. + * + * @param columns The new number of columns in the field. + * + * @exception IllegalArgumentException If columns is less than zero. + */ + public synchronized void setColumns(int columns) + { + if (columns < 0) + throw new IllegalArgumentException("Value is less than zero: " + + columns); - return peer.getMinimumSize (columns); -} + this.columns = columns; + // FIXME: How to we communicate this to our peer? + } -/*************************************************************************/ + /** + * Returns the character that is echoed to the screen when a text + * field is protected (such as when a password is being entered). + * + * @return The echo character for this text field. + */ + public char getEchoChar() + { + return(echoChar); + } -/** - * Returns the preferred size for this text field. - * - * @return The preferred size for this text field. - */ -public Dimension -getPreferredSize() -{ - return getPreferredSize (getColumns ()); -} + /** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + */ + public void setEchoChar(char echoChar) + { + setEchoCharacter(echoChar); + } -/*************************************************************************/ + /** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + * + * @deprecated This method is deprecated in favor of + * <code>setEchoChar()</code> + */ + public void setEchoCharacter(char echoChar) + { + this.echoChar = echoChar; -/** - * Returns the preferred size of a text field with the specified number - * of columns. - * - * @param columns The number of columns to get the preferred size for. - */ -public Dimension -getPreferredSize(int columns) -{ - return preferredSize (columns); -} + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer != null) + peer.setEchoChar (echoChar); + } -/*************************************************************************/ + /** + * Tests whether or not this text field has an echo character set + * so that characters the user type are not echoed to the screen. + * + * @return <code>true</code> if an echo character is set, + * <code>false</code> otherwise. + */ + public boolean echoCharIsSet() + { + if (echoChar == '\u0000') + return(false); + else + return(true); + } -/** - * Returns the preferred size for this text field. - * - * @return The preferred size for this text field. - * - * @deprecated This method is deprecated in favor of - * <code>getPreferredSize()</code>. - */ -public Dimension -preferredSize() -{ - return preferredSize (getColumns ()); -} + /** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + */ + public Dimension getMinimumSize() + { + return getMinimumSize (getColumns ()); + } -/*************************************************************************/ + /** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + */ + public Dimension getMinimumSize(int columns) + { + return minimumSize(columns); + } -/** - * Returns the preferred size of a text field with the specified number - * of columns. - * - * @param columns The number of columns to get the preferred size for. - * - * @deprecated This method is deprecated in favor of - * <code>getPreferredSize(int)</code>. - */ -public Dimension -preferredSize(int columns) -{ - TextFieldPeer peer = (TextFieldPeer) getPeer (); - if (peer == null) - return new Dimension (0, 0); + /** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize()</code>. + */ + public Dimension minimumSize() + { + return minimumSize(getColumns ()); + } - return peer.getPreferredSize (columns); -} + /** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + * + * @deprecated This method is deprecated in favor of + * <code>getMinimumSize(int)</code>. + */ + public Dimension minimumSize(int columns) + { + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return null; // FIXME: What do we do if there is no peer? -/*************************************************************************/ + return peer.getMinimumSize (columns); + } -/** - * Notifies this object that it should create its native peer. - */ -public void -addNotify() -{ - if (getPeer() != null) - return; + /** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + */ + public Dimension getPreferredSize() + { + return getPreferredSize(getColumns ()); + } - setPeer((ComponentPeer)getToolkit().createTextField(this)); - super.addNotify(); -} + /** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + */ + public Dimension getPreferredSize(int columns) + { + return preferredSize(columns); + } -/*************************************************************************/ + /** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize()</code>. + */ + public Dimension preferredSize() + { + return preferredSize(getColumns ()); + } -/** - * Addes a new listener to the list of action listeners for this - * object. - * - * @param listener The listener to add to the list. - */ -public synchronized void -addActionListener(ActionListener listener) -{ - action_listeners = AWTEventMulticaster.add(action_listeners, listener); + /** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + * + * @deprecated This method is deprecated in favor of + * <code>getPreferredSize(int)</code>. + */ + public Dimension preferredSize(int columns) + { + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return new Dimension (0, 0); - enableEvents(AWTEvent.ACTION_EVENT_MASK); -} + return peer.getPreferredSize (columns); + } -/*************************************************************************/ + /** + * Notifies this object that it should create its native peer. + */ + public void addNotify() + { + if (getPeer() != null) + return; -/** - * Removes the specified listener from the list of action listeners - * for this object. - * - * @param listener The listener to remove from the list. - */ -public synchronized void -removeActionListener(ActionListener listener) -{ - action_listeners = AWTEventMulticaster.remove(action_listeners, listener); -} + setPeer((ComponentPeer)getToolkit().createTextField(this)); + super.addNotify(); + } -/*************************************************************************/ + /** + * Addes a new listener to the list of action listeners for this + * object. + * + * @param listener The listener to add to the list. + */ + public synchronized void addActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.add(action_listeners, listener); -/** - * Processes the specified event. If the event is an instance of - * <code>ActionEvent</code> then <code>processActionEvent()</code> is - * called to process it, otherwise the event is sent to the - * superclass. - * - * @param event The event to process. - */ -protected void -processEvent(AWTEvent event) -{ - if (event instanceof ActionEvent) - processActionEvent((ActionEvent)event); - else - super.processEvent(event); -} + enableEvents(AWTEvent.ACTION_EVENT_MASK); + } -/*************************************************************************/ + /** + * Removes the specified listener from the list of action listeners + * for this object. + * + * @param listener The listener to remove from the list. + */ + public synchronized void removeActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); + } -/** - * Processes an action event by calling any registered listeners. - * Note to subclasses: This method is not called unless action events - * are enabled on this object. This will be true if any listeners - * are registered, or if action events were specifically enabled - * using <code>enableEvents()</code>. - * - * @param event The event to process. - */ -protected void -processActionEvent(ActionEvent event) -{ - if (action_listeners != null) - action_listeners.actionPerformed(event); -} + /** + * Processes the specified event. If the event is an instance of + * <code>ActionEvent</code> then <code>processActionEvent()</code> is + * called to process it, otherwise the event is sent to the + * superclass. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else + super.processEvent(event); + } -void -dispatchEventImpl(AWTEvent e) -{ - if (e.id <= ActionEvent.ACTION_LAST - && e.id >= ActionEvent.ACTION_FIRST - && (action_listeners != null - || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) - processEvent(e); - else - super.dispatchEventImpl(e); -} + /** + * Processes an action event by calling any registered listeners. + * Note to subclasses: This method is not called unless action events + * are enabled on this object. This will be true if any listeners + * are registered, or if action events were specifically enabled + * using <code>enableEvents()</code>. + * + * @param event The event to process. + */ + protected void processActionEvent(ActionEvent event) + { + if (action_listeners != null) + action_listeners.actionPerformed(event); + } -/*************************************************************************/ + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } -/** + /** * Returns a debug string for this object. * * @return A debug string for this object. */ -protected String -paramString() -{ - return(getClass().getName() + "(columns=" + getColumns() + ",echoChar=" + - getEchoChar()); -} + protected String paramString() + { + return(getClass().getName() + "(columns=" + getColumns() + ",echoChar=" + + getEchoChar()); + } /** * Returns an array of all the objects currently registered as FooListeners @@ -521,6 +440,21 @@ paramString() { return (ActionListener[]) getListeners (ActionListener.class); } + + /** + * Generate a unique name for this <code>TextField</code>. + * + * @return A unique name for this <code>TextField</code>. + */ + String generateName() + { + return "textfield" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_textfield_number++; + } protected class AccessibleAWTTextField extends AccessibleAWTTextComponent { @@ -541,4 +475,4 @@ paramString() return new AccessibleAWTTextField(); } -} // class TextField +} diff --git a/libjava/classpath/java/awt/Toolkit.java b/libjava/classpath/java/awt/Toolkit.java index 282e50d2c50..2842091c139 100644 --- a/libjava/classpath/java/awt/Toolkit.java +++ b/libjava/classpath/java/awt/Toolkit.java @@ -70,6 +70,7 @@ import java.awt.peer.ListPeer; import java.awt.peer.MenuBarPeer; import java.awt.peer.MenuItemPeer; import java.awt.peer.MenuPeer; +import java.awt.peer.MouseInfoPeer; import java.awt.peer.PanelPeer; import java.awt.peer.PopupMenuPeer; import java.awt.peer.ScrollPanePeer; @@ -332,6 +333,18 @@ public abstract class Toolkit protected abstract MenuItemPeer createMenuItem(MenuItem target); /** + * Returns a MouseInfoPeer. + * The default implementation of this method throws + * UnsupportedOperationException. + * + * Toolkit implementations should overload this if possible, however. + */ + protected MouseInfoPeer getMouseInfoPeer() + { + throw new UnsupportedOperationException("No mouse info peer."); + } + + /** * Creates a peer object for the specified <code>FileDialog</code>. * * @param target The <code>FileDialog</code> to create the peer for. @@ -695,6 +708,14 @@ public abstract class Toolkit public PrintJob getPrintJob(Frame frame, String title, JobAttributes jobAttr, PageAttributes pageAttr) { + // FIXME: it is possible this check may be removed + // if this method, when written, always delegates to + // getPrintJob(Frame, String, Properties). + SecurityManager sm; + sm = System.getSecurityManager(); + if (sm != null) + sm.checkPrintJobAccess(); + return null; } diff --git a/libjava/classpath/java/awt/Window.java b/libjava/classpath/java/awt/Window.java index 8bc4715aed5..8885821811d 100644 --- a/libjava/classpath/java/awt/Window.java +++ b/libjava/classpath/java/awt/Window.java @@ -39,8 +39,6 @@ exception statement from your version. */ package java.awt; import java.awt.event.ComponentEvent; -import java.awt.event.FocusEvent; -import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.awt.event.WindowListener; @@ -80,6 +78,8 @@ public class Window extends Container implements Accessible private int state = 0; /** @since 1.4 */ private boolean focusableWindowState = true; + /** @since 1.5 */ + private boolean alwaysOnTop = false; // A list of other top-level windows owned by this window. private transient Vector ownedWindows = new Vector(); @@ -130,7 +130,6 @@ public class Window extends Container implements Accessible // cycle roots. focusCycleRoot = true; setLayout(new BorderLayout()); - addWindowFocusListener(); GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment(); graphicsConfiguration = g.getDefaultScreenDevice().getDefaultConfiguration(); @@ -142,67 +141,6 @@ public class Window extends Container implements Accessible graphicsConfiguration = gc; } - private void addWindowFocusListener() - { - addWindowFocusListener(new WindowAdapter() - { - public void windowGainedFocus(WindowEvent event) - { - EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); - if (windowFocusOwner != null) - { - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner(); - if (currentFocusOwner != null) - { - eq.postEvent(new FocusEvent(currentFocusOwner, - FocusEvent.FOCUS_LOST, false, - windowFocusOwner)); - eq.postEvent(new FocusEvent(windowFocusOwner, - FocusEvent.FOCUS_GAINED, false, - currentFocusOwner)); - } - else - eq.postEvent(new FocusEvent(windowFocusOwner, - FocusEvent.FOCUS_GAINED, false)); - } - } - else - eq.postEvent(new FocusEvent(Window.this, FocusEvent.FOCUS_GAINED, - false)); - } - - public void windowLostFocus(WindowEvent event) - { - EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); - if (windowFocusOwner != null) - { - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner(); - if (currentFocusOwner != null) - { - eq.postEvent(new FocusEvent(currentFocusOwner, - FocusEvent.FOCUS_GAINED, false, - windowFocusOwner)); - eq.postEvent(new FocusEvent(windowFocusOwner, - FocusEvent.FOCUS_LOST, false, - currentFocusOwner)); - } - else - eq.postEvent(new FocusEvent(windowFocusOwner, - FocusEvent.FOCUS_LOST, false)); - } - } - else - eq.postEvent(new FocusEvent(Window.this, FocusEvent.FOCUS_LOST, false)); - } - }); - } - /** * Initializes a new instance of <code>Window</code> with the specified * parent. The window will initially be invisible. @@ -420,13 +358,17 @@ public class Window extends Container implements Accessible /** * Sends this window to the back so that all other windows display in * front of it. + * + * If the window is set to be always-on-top, this will remove its + * always-on-top status. */ public void toBack() { if (peer != null) { - WindowPeer wp = (WindowPeer) peer; - wp.toBack(); + if( alwaysOnTop ) + setAlwaysOnTop( false ); + ( (WindowPeer) peer ).toBack(); } } @@ -437,10 +379,7 @@ public class Window extends Container implements Accessible public void toFront() { if (peer != null) - { - WindowPeer wp = (WindowPeer) peer; - wp.toFront(); - } + ( (WindowPeer) peer ).toFront(); } /** @@ -1236,6 +1175,55 @@ public class Window extends Container implements Accessible } /** + * Returns whether the Windows is an always-on-top window, + * meaning whether the window can be obscured by other windows or not. + * + * @return <code>true</code> if the windows is always-on-top, + * <code>false</code> otherwise. + * @since 1.5 + */ + public final boolean isAlwaysOnTop() + { + return alwaysOnTop; + } + + /** + * Sets the always-on-top state of this window (if supported). + * + * Setting a window to always-on-top means it will not be obscured + * by any other windows (with the exception of other always-on-top + * windows). Not all platforms may support this. + * + * If an window's always-on-top status is changed to false, the window + * will remain at the front but not be anchored there. + * + * Calling toBack() on an always-on-top window will change its + * always-on-top status to false. + * + * @since 1.5 + */ + public final void setAlwaysOnTop(boolean alwaysOnTop) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission( new AWTPermission("setWindowAlwaysOnTop") ); + + if( this.alwaysOnTop == alwaysOnTop ) + return; + + if( alwaysOnTop ) + toFront(); + + firePropertyChange("alwaysOnTop", this.alwaysOnTop, alwaysOnTop ); + this.alwaysOnTop = alwaysOnTop; + + if (peer != null) + ( (WindowPeer) peer).updateAlwaysOnTop(); + else + System.out.println("Null peer?!"); + } + + /** * Generate a unique name for this window. * * @return A unique name for this window. diff --git a/libjava/classpath/java/awt/datatransfer/Clipboard.java b/libjava/classpath/java/awt/datatransfer/Clipboard.java index 5fa1d1ab134..2029e2c351b 100644 --- a/libjava/classpath/java/awt/datatransfer/Clipboard.java +++ b/libjava/classpath/java/awt/datatransfer/Clipboard.java @@ -1,5 +1,5 @@ /* Clipboard.java -- Class for transferring data via cut and paste. - Copyright (C) 1999, 2001, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2001, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -182,6 +182,9 @@ public class Clipboard public void addFlavorListener(FlavorListener listener) { + if (listener == null) + return; + synchronized(listeners) { listeners.add(listener); @@ -190,6 +193,9 @@ public class Clipboard public void removeFlavorListener(FlavorListener listener) { + if (listener == null) + return; + synchronized(listeners) { listeners.remove(listener); diff --git a/libjava/classpath/java/awt/datatransfer/DataFlavor.java b/libjava/classpath/java/awt/datatransfer/DataFlavor.java index 5944c2eb7ec..0228cd5786d 100644 --- a/libjava/classpath/java/awt/datatransfer/DataFlavor.java +++ b/libjava/classpath/java/awt/datatransfer/DataFlavor.java @@ -1,5 +1,5 @@ /* DataFlavor.java -- A type of data to transfer via the clipboard. - Copyright (C) 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2001, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -47,6 +47,7 @@ import java.io.InputStreamReader; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Reader; +import java.io.Serializable; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @@ -197,31 +198,37 @@ public class DataFlavor implements java.io.Externalizable, Cloneable throw new ClassNotFoundException(className); } - private static Class getRepresentationClassFromMime(String mimeString, + private static Class getRepresentationClassFromMimeThrows(String mimeString, ClassLoader classLoader) + throws ClassNotFoundException { String classname = getParameter("class", mimeString); if (classname != null) - { - try - { - return tryToLoadClass(classname, classLoader); - } - catch(Exception e) - { - IllegalArgumentException iae; - iae = new IllegalArgumentException("mimeString: " - + mimeString - + " classLoader: " - + classLoader); - iae.initCause(e); - throw iae; - } - } + return tryToLoadClass(classname, classLoader); else return java.io.InputStream.class; } - + + // Same as above, but wraps any ClassNotFoundExceptions + private static Class getRepresentationClassFromMime(String mimeString, + ClassLoader classLoader) + { + try + { + return getRepresentationClassFromMimeThrows(mimeString, classLoader); + } + catch(ClassNotFoundException cnfe) + { + IllegalArgumentException iae; + iae = new IllegalArgumentException("mimeString: " + + mimeString + + " classLoader: " + + classLoader); + iae.initCause(cnfe); + throw iae; + } + } + /** * Returns the value of the named MIME type parameter, or <code>null</code> * if the parameter does not exist. Given the parameter name and the mime @@ -240,7 +247,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable String value = mimeString.substring(idx + paramName.length() + 1); - idx = value.indexOf(" "); + idx = value.indexOf(";"); if (idx == -1) return(value); else @@ -328,6 +335,14 @@ public class DataFlavor implements java.io.Externalizable, Cloneable { this.representationClass = representationClass; this.mimeType = mimeType; + + // Do some simple validity checks + String type = getPrimaryType() + "/" + getSubType(); + if (type.indexOf(' ') != -1 + || type.indexOf('=') != -1 + || type.indexOf(';') != -1) + throw new IllegalArgumentException(mimeType); + if (humanPresentableName != null) this.humanPresentableName = humanPresentableName; else @@ -375,7 +390,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable ClassLoader classLoader) throws ClassNotFoundException { - this(getRepresentationClassFromMime(mimeType, classLoader), + this(getRepresentationClassFromMimeThrows(mimeType, classLoader), mimeType, humanPresentableName); } @@ -417,7 +432,8 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public DataFlavor(String mimeType) throws ClassNotFoundException { - this(mimeType, null); + this(getRepresentationClassFromMimeThrows(mimeType, null), + mimeType, null); } /** @@ -567,7 +583,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isRepresentationClassInputStream() { - return representationClass.getName().equals("java.io.InputStream"); + return InputStream.class.isAssignableFrom(representationClass); } /** @@ -579,17 +595,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isRepresentationClassSerializable() { - Class[] interfaces = representationClass.getInterfaces(); - - int i = 0; - while (i < interfaces.length) - { - if (interfaces[i].getName().equals("java.io.Serializable")) - return true; - ++i; - } - - return false; + return Serializable.class.isAssignableFrom(representationClass); } /** @@ -634,8 +640,10 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isFlavorJavaFileListType() { - if (mimeType.equals(javaFileListFlavor.mimeType) - && representationClass.equals(javaFileListFlavor.representationClass)) + if (getPrimaryType().equals(javaFileListFlavor.getPrimaryType()) + && getSubType().equals(javaFileListFlavor.getSubType()) + && javaFileListFlavor.representationClass + .isAssignableFrom(representationClass)) return true; return false ; @@ -666,7 +674,11 @@ public class DataFlavor implements java.io.Externalizable, Cloneable /** * This method test the specified <code>DataFlavor</code> for equality * against this object. This will be true if the MIME type and - * representation type are the equal. + * representation class are the equal. If the primary type is 'text' + * then also the value of the charset parameter is compared. In such a + * case when the charset parameter isn't given then the charset is + * assumed to be equal to the default charset of the platform. All + * other parameters are ignored. * * @param flavor The <code>DataFlavor</code> to test against. * @@ -677,12 +689,34 @@ public class DataFlavor implements java.io.Externalizable, Cloneable { if (flavor == null) return false; - - if (! this.mimeType.toLowerCase().equals(flavor.mimeType.toLowerCase())) + + String primary = getPrimaryType(); + if (! primary.equals(flavor.getPrimaryType())) return false; - + + String sub = getSubType(); + if (! sub.equals(flavor.getSubType())) + return false; + if (! this.representationClass.equals(flavor.representationClass)) return false; + + if (primary.equals("text")) + if (! isRepresentationClassCharBuffer() + && ! isRepresentationClassReader() + && representationClass != java.lang.String.class + && ! (representationClass.isArray() + && representationClass.getComponentType() == Character.TYPE)) + { + String charset = getParameter("charset"); + String otherset = flavor.getParameter("charset"); + String defaultset = Charset.defaultCharset().name(); + + if (charset == null || charset.equals(defaultset)) + return (otherset == null || otherset.equals(defaultset)); + + return charset.equals(otherset); + } return true; } diff --git a/libjava/classpath/java/awt/dnd/DragGestureEvent.java b/libjava/classpath/java/awt/dnd/DragGestureEvent.java index 9f2bc7c98b8..351ae540072 100644 --- a/libjava/classpath/java/awt/dnd/DragGestureEvent.java +++ b/libjava/classpath/java/awt/dnd/DragGestureEvent.java @@ -48,13 +48,6 @@ import java.util.EventObject; import java.util.Iterator; import java.util.List; -/** - * STUBBED - * @see DragGestureRecognizer - * @see DragGestureListener - * @see DragSource - * @since 1.2 - */ public class DragGestureEvent extends EventObject { /** @@ -66,52 +59,121 @@ public class DragGestureEvent extends EventObject private Component component; private final Point origin; private final int action; + private List events; + private DragGestureRecognizer dgr; + /** + * Constructs a new DragGestureEvent. + * @param dgr - DragGestureRecognizer firing this event + * @param action - user's preferred action + * @param origin - origin of the drag + * @param events - List of events that make up the gesture + * @throws IllegalArgumentException - if input parameters are null + */ public DragGestureEvent(DragGestureRecognizer dgr, int action, Point origin, List events) - { + { super(dgr); - if (origin == null || events == null) + if (origin == null || events == null || dgr == null) throw new IllegalArgumentException(); + this.origin = origin; this.action = action; + this.events = events; + this.dgr = dgr; + this.component = dgr.getComponent(); + this.dragSource = dgr.getDragSource(); } + /** + * Returns the source casted as a DragGestureRecognizer. + * + * @return the source casted as a DragGestureRecognizer. + */ public DragGestureRecognizer getSourceAsDragGestureRecognizer() { - return (DragGestureRecognizer) source; + return (DragGestureRecognizer) getSource(); } + + /** + * Returns the Component corresponding to this. + * + * @return the Component corresponding to this. + */ public Component getComponent() { - return null; + return component; } + + /** + * Gets the DragSource corresponding to this. + * + * @return the DragSource corresponding to this. + */ public DragSource getDragSource() { - return null; + return dragSource; } + + /** + * Returns the origin of the drag. + * + * @return the origin of the drag. + */ public Point getDragOrigin() { return origin; } + + /** + * Gets an iterator representation of the List of events. + * + * @return an iterator representation of the List of events. + */ public Iterator iterator() { - return null; + return events.iterator(); } + + /** + * Gets an array representation of the List of events. + * + * @return an array representation of the List of events. + */ public Object[] toArray() { - return null; + return events.toArray(); } + + /** + * Gets an array representation of the List of events. + * + * @param array - the array to store the events in. + * @return an array representation of the List of events. + */ public Object[] toArray(Object[] array) { - return array; + return events.toArray(array); } + + /** + * Gets the user's preferred action. + * + * @return the user's preferred action. + */ public int getDragAction() { - return 0; + return action; } + + /** + * Get the event that triggered this gesture. + * + * @return the event that triggered this gesture. + */ public InputEvent getTriggerEvent() { - return null; + return dgr.getTriggerEvent(); } /** @@ -152,5 +214,6 @@ public class DragGestureEvent extends EventObject public void startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable trans, DragSourceListener l) { + dragSource.startDrag(this, dragCursor, dragImage, imageOffset, trans, l); } } // class DragGestureEvent diff --git a/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java index 145a24a3850..32bbc56da5d 100644 --- a/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java +++ b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java @@ -131,6 +131,7 @@ public abstract class DragGestureRecognizer implements Serializable throws NotImplementedException { events = new ArrayList(); + // FIXME: Not implemented fully. } /** diff --git a/libjava/classpath/java/awt/dnd/DragSource.java b/libjava/classpath/java/awt/dnd/DragSource.java index 05eb6709d47..48fa2388ee2 100644 --- a/libjava/classpath/java/awt/dnd/DragSource.java +++ b/libjava/classpath/java/awt/dnd/DragSource.java @@ -38,6 +38,8 @@ exception statement from your version. */ package java.awt.dnd; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.Cursor; import java.awt.GraphicsEnvironment; @@ -70,9 +72,12 @@ public class DragSource implements Serializable public static final Cursor DefaultLinkNoDrop = null; private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap (); - private transient DragSourceListener dragSourceListener; private transient DragSourceMotionListener dragSourceMotionListener; + + private static DragSource ds; + private DragSourceContextPeer peer; + private DragSourceContext context; /** * Initializes the drag source. @@ -82,19 +87,34 @@ public class DragSource implements Serializable public DragSource() { if (GraphicsEnvironment.isHeadless()) - throw new HeadlessException (); + { + ds = null; + throw new HeadlessException(); + } } /** + * Gets the default drag source. + * * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. */ public static DragSource getDefaultDragSource() { - return new DragSource(); + if (GraphicsEnvironment.isHeadless()) + { + ds = null; + throw new HeadlessException(); + } + + if (ds == null) + ds = new DragSource(); + return ds; } public static boolean isDragImageSupported() + throws NotImplementedException { + // FIXME: Implement this return false; } @@ -110,6 +130,43 @@ public class DragSource implements Serializable Transferable trans, DragSourceListener dsl, FlavorMap map) { + // http://www.javaworld.com/javaworld/jw-03-1999/jw-03-dragndrop.html + + // This function creates a DragSourceContext object. This object tracks the + // state of the operation by listening to a native peer. In this situation, + // the DragSource may be obtained from the event or by an instance variable. + // This function also creates a new DragSourceContextPeer. + + // This function sends the same message to the context, which then forwards + // it to the peer, passing itself as a parameter. Now, the native system has + // access to the Transferable through the context. + + // FIXME: Add check to determine if dragging. + + try + { + flavorMap = map; + + if (peer == null) + peer = Toolkit.getDefaultToolkit().createDragSourceContextPeer(trigger); + + if (context == null) + context = createDragSourceContext(peer, trigger, + dragCursor, + dragImage, + imageOffset, trans, + dsl); + + if (peer == null) + throw new InvalidDnDOperationException(); + + peer.startDrag(context, dragCursor, dragImage, imageOffset); + } + catch (Exception e) + { + throw new InvalidDnDOperationException("Drag and Drop system is " + + "unable to initiate a drag operation."); + } } /** @@ -156,7 +213,7 @@ public class DragSource implements Serializable /** * Creates the DragSourceContext to handle this drag. * - * @exception IllegalArgumentException FIXME + * @exception IllegalArgumentException * @exception NullPointerException If dscp, dgl, dragImage or t is null. */ protected DragSourceContext @@ -164,7 +221,7 @@ public class DragSource implements Serializable Cursor cursor, Image image, Point offset, Transferable t, DragSourceListener dsl) { - return null; + return new DragSourceContext(peer, dge, cursor, image, offset, t, dsl); } public FlavorMap getFlavorMap() @@ -172,42 +229,22 @@ public class DragSource implements Serializable return flavorMap; } - /** - * Dummy DragGestureRecognizer when Toolkit doesn't support drag and drop. - */ - static class NoDragGestureRecognizer extends DragGestureRecognizer - { - NoDragGestureRecognizer(DragSource ds, Component c, int actions, - DragGestureListener dgl) - { - super(ds, c, actions, dgl); - } - - protected void registerListeners() { } - protected void unregisterListeners() { } - } - - public DragGestureRecognizer - createDragGestureRecognizer(Class recognizer, Component c, int actions, - DragGestureListener dgl) + public DragGestureRecognizer createDragGestureRecognizer(Class recognizer, + Component c, + int actions, + DragGestureListener dgl) { - DragGestureRecognizer dgr; - dgr = Toolkit.getDefaultToolkit () - .createDragGestureRecognizer (recognizer, this, c, actions, - dgl); - - if (dgr == null) - dgr = new NoDragGestureRecognizer(this, c, actions, dgl); - - return dgr; + return Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizer, + this, c, + actions, dgl); } - public DragGestureRecognizer - createDefaultDragGestureRecognizer(Component c, int actions, - DragGestureListener dgl) + public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c, + int actions, + DragGestureListener dgl) { - return createDragGestureRecognizer (MouseDragGestureRecognizer.class, c, - actions, dgl); + return createDragGestureRecognizer(MouseDragGestureRecognizer.class, c, + actions, dgl); } /** @@ -275,4 +312,17 @@ public class DragSource implements Serializable // Return an empty EventListener array. return new EventListener [0]; } + + /** + * TODO + * @return + * + * @since 1.5 + */ + public static int getDragThreshold() + throws NotImplementedException + { + // FIXME: Not implemented. + return 4; + } } // class DragSource diff --git a/libjava/classpath/java/awt/dnd/DragSourceContext.java b/libjava/classpath/java/awt/dnd/DragSourceContext.java index 88607b090ea..1fee5c0c304 100644 --- a/libjava/classpath/java/awt/dnd/DragSourceContext.java +++ b/libjava/classpath/java/awt/dnd/DragSourceContext.java @@ -70,8 +70,8 @@ public class DragSourceContext private Transferable transferable; private DragGestureEvent trigger; private DragSourceListener dragSourceListener; - private boolean useCustomCursor; // FIXME: currently unused but needed for serialization. - private int sourceActions; // FIXME: currently unused but needed for serialization. + private boolean useCustomCursor; + private int sourceActions; private Image image; private Point offset; @@ -82,16 +82,17 @@ public class DragSourceContext * are null, the drag action for the trigger event is DnDConstants.ACTION_NONE * or if the source actions for the DragGestureRecognizer associated with the * trigger event are equal to DnDConstants.ACTION_NONE. - * @exception NullPointerException If peer or trigger is null. + * @exception NullPointerException If peer, trans or trigger is null or if the + * image is not null but the offset is. */ public DragSourceContext (DragSourceContextPeer peer, DragGestureEvent trigger, Cursor cursor, Image image, Point offset, Transferable trans, DragSourceListener dsl) - throws NotImplementedException - { + { if (peer == null - || trigger == null) + || trigger == null || trans == null + || (image != null && offset == null)) throw new NullPointerException (); if (trigger.getComponent () == null @@ -108,37 +109,77 @@ public class DragSourceContext this.offset = offset; this.transferable = trans; this.dragSourceListener = dsl; + this.sourceActions = trigger.getSourceAsDragGestureRecognizer().getSourceActions(); - throw new Error ("not implemented"); + setCursor(cursor); + updateCurrentCursor(trigger.getDragAction(), sourceActions, DEFAULT); } + /** + * Returns the DragSource object associated with the + * DragGestureEvent. + * + * @return the DragSource associated with the trigger. + */ public DragSource getDragSource() { return trigger.getDragSource (); } + /** + * Returns the component associated with this. + * + * @return the component associated with the trigger. + */ public Component getComponent() { return trigger.getComponent (); } + /** + * Gets the trigger associated with this. + * + * @return the trigger. + */ public DragGestureEvent getTrigger() { return trigger; } + /** + * Returns the source actions for the DragGestureRecognizer. + * + * @return the source actions for DragGestureRecognizer. + */ public int getSourceActions() { - return trigger.getSourceAsDragGestureRecognizer ().getSourceActions (); + if (sourceActions == 0) + sourceActions = trigger.getSourceAsDragGestureRecognizer().getSourceActions(); + return sourceActions; } - public void setCursor (Cursor cursor) - throws NotImplementedException + /** + * Sets the cursor for this drag operation to the specified cursor. + * + * @param cursor c - the Cursor to use, or null to use the default drag + * cursor. + */ + public void setCursor(Cursor cursor) { + if (cursor == null) + useCustomCursor = false; + else + useCustomCursor = true; this.cursor = cursor; - // FIXME: Check if we need to do more here + peer.setCursor(cursor); } + /** + * Returns the current cursor or null if the default + * drag cursor is used. + * + * @return the current cursor or null. + */ public Cursor getCursor() { return cursor; @@ -165,48 +206,160 @@ public class DragSourceContext dragSourceListener = null; } + /** + * This function tells the peer that the DataFlavors have been modified. + */ public void transferablesFlavorsChanged() - throws NotImplementedException { + peer.transferablesFlavorsChanged(); } + /** + * Calls dragEnter on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ public void dragEnter(DragSourceDragEvent e) - throws NotImplementedException { + if (dragSourceListener != null) + dragSourceListener.dragEnter(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragEnter(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), ENTER); } + /** + * Calls dragOver on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ public void dragOver(DragSourceDragEvent e) - throws NotImplementedException { + if (dragSourceListener != null) + dragSourceListener.dragOver(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragOver(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), OVER); } - + + /** + * Calls dragExit on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceEvent + */ public void dragExit(DragSourceEvent e) - throws NotImplementedException { + if (dragSourceListener != null) + dragSourceListener.dragExit(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragExit(e); + + updateCurrentCursor(0, 0, DEFAULT); } + /** + * Calls dropActionChanged on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ public void dropActionChanged(DragSourceDragEvent e) - throws NotImplementedException { + if (dragSourceListener != null) + dragSourceListener.dropActionChanged(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dropActionChanged(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), CHANGED); } + /** + * Calls dragDropEnd on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDropEvent + */ public void dragDropEnd(DragSourceDropEvent e) - throws NotImplementedException { + if (dragSourceListener != null) + dragSourceListener.dragDropEnd(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragDropEnd(e); } + /** + * Calls dragMouseMoved on the listeners registered with the DragSource. + * + * @param e - the DragSourceDragEvent + */ public void dragMouseMoved(DragSourceDragEvent e) - throws NotImplementedException { + DragSource ds = getDragSource(); + DragSourceMotionListener[] dsml = ds.getDragSourceMotionListeners(); + for (int i = 0; i < dsml.length; i++) + dsml[i].dragMouseMoved(e); } + /** + * Returns the Transferable set with this object. + * + * @return the transferable. + */ public Transferable getTransferable() { return transferable; } + /** + * This function sets the drag cursor for the specified operation, actions and + * status if the default drag cursor is active. Otherwise, the cursor is not + * updated in any way. + * + * @param dropOp - the current operation. + * @param targetAct - the supported actions. + * @param status - the status of the cursor (constant). + */ protected void updateCurrentCursor(int dropOp, int targetAct, int status) throws NotImplementedException { + // FIXME: Not implemented fully + if (!useCustomCursor) + { + Cursor cursor = null; + switch (status) + { + case ENTER: + break; + case CHANGED: + break; + case OVER: + break; + default: + break; + } + + this.cursor = cursor; + peer.setCursor(cursor); + } } } // class DragSourceContext diff --git a/libjava/classpath/java/awt/dnd/DropTarget.java b/libjava/classpath/java/awt/dnd/DropTarget.java index b0d4c2ae7a1..a3650567f09 100644 --- a/libjava/classpath/java/awt/dnd/DropTarget.java +++ b/libjava/classpath/java/awt/dnd/DropTarget.java @@ -38,13 +38,18 @@ exception statement from your version. */ package java.awt.dnd; +import gnu.classpath.NotImplementedException; + import java.awt.Component; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.Point; import java.awt.datatransfer.FlavorMap; +import java.awt.dnd.peer.DropTargetPeer; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; import java.io.Serializable; import java.util.EventListener; import java.util.TooManyListenersException; @@ -79,19 +84,25 @@ public class DropTarget } protected void stop () + throws NotImplementedException { + // FIXME: implement this } public void actionPerformed (ActionEvent e) + throws NotImplementedException { + // FIXME: implement this } } private Component component; private FlavorMap flavorMap; private int actions; + private DropTargetPeer peer; private DropTargetContext dropTargetContext; private DropTargetListener dropTargetListener; + private DropTarget.DropTargetAutoScroller autoscroller; private boolean active = true; /** @@ -150,12 +161,15 @@ public class DropTarget if (GraphicsEnvironment.isHeadless ()) throw new HeadlessException (); - component = c; - actions = i; + setComponent(c); + setDefaultActions(i); dropTargetListener = dtl; flavorMap = fm; setActive (b); + + if (c != null) + c.setDropTarget(this); } /** @@ -211,33 +225,46 @@ public class DropTarget public void addDropTargetListener (DropTargetListener dtl) throws TooManyListenersException { + if (dropTargetListener != null) + throw new TooManyListenersException (); + dropTargetListener = dtl; } public void removeDropTargetListener(DropTargetListener dtl) { - // FIXME: Do we need to do something with dtl ? - dropTargetListener = null; + if (dropTargetListener != null) + dropTargetListener = null; } public void dragEnter(DropTargetDragEvent dtde) { + if (dropTargetListener != null) + dropTargetListener.dragEnter(dtde); } public void dragOver(DropTargetDragEvent dtde) { + if (dropTargetListener != null) + dropTargetListener.dragOver(dtde); } public void dropActionChanged(DropTargetDragEvent dtde) { + if (dropTargetListener != null) + dropTargetListener.dropActionChanged(dtde); } public void dragExit(DropTargetEvent dte) { + if (dropTargetListener != null) + dropTargetListener.dragExit(dte); } public void drop(DropTargetDropEvent dtde) { + if (dropTargetListener != null) + dropTargetListener.drop(dtde); } public FlavorMap getFlavorMap() @@ -250,12 +277,29 @@ public class DropTarget flavorMap = fm; } - public void addNotify(java.awt.peer.ComponentPeer peer) + public void addNotify(ComponentPeer p) { + Component c = component; + while (c != null && p instanceof LightweightPeer) + { + p = c.getPeer(); + c = c.getParent(); + } + + if (p instanceof DropTargetPeer) + { + peer = ((DropTargetPeer) p); + peer.addDropTarget(this); + } + else + peer = null; } - public void removeNotify(java.awt.peer.ComponentPeer peer) + public void removeNotify(ComponentPeer p) { + ((DropTargetPeer) peer).removeDropTarget(this); + peer = null; + p = null; } public DropTargetContext getDropTargetContext() @@ -268,24 +312,34 @@ public class DropTarget protected DropTargetContext createDropTargetContext() { - return new DropTargetContext (this); + if (dropTargetContext == null) + dropTargetContext = new DropTargetContext (this); + + return dropTargetContext; } protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller (Component c, Point p) { - return new DropTarget.DropTargetAutoScroller (c, p); + if (autoscroller == null) + autoscroller = new DropTarget.DropTargetAutoScroller (c, p); + + return autoscroller; } protected void initializeAutoscrolling(Point p) { + createDropTargetAutoScroller (component, p); } protected void updateAutoscroll(Point dragCursorLocn) { + if (autoscroller != null) + autoscroller.updateLocation(dragCursorLocn); } protected void clearAutoscroll() { + autoscroller = null; } } // class DropTarget diff --git a/libjava/classpath/java/awt/dnd/DropTargetContext.java b/libjava/classpath/java/awt/dnd/DropTargetContext.java index 4a26d904880..31945c34bb1 100644 --- a/libjava/classpath/java/awt/dnd/DropTargetContext.java +++ b/libjava/classpath/java/awt/dnd/DropTargetContext.java @@ -37,12 +37,11 @@ exception statement from your version. */ package java.awt.dnd; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.peer.DropTargetContextPeer; import java.io.IOException; import java.io.Serializable; import java.util.Arrays; @@ -86,7 +85,7 @@ public class DropTargetContext implements Serializable private DropTarget dropTarget; private int targetActions; - private java.awt.dnd.peer.DropTargetContextPeer dtcp; + private DropTargetContextPeer dtcp; // package private DropTargetContext(DropTarget dropTarget) @@ -104,7 +103,7 @@ public class DropTargetContext implements Serializable return dropTarget.getComponent(); } - public void addNotify(java.awt.dnd.peer.DropTargetContextPeer dtcp) + public void addNotify(DropTargetContextPeer dtcp) { this.dtcp = dtcp; } @@ -130,39 +129,39 @@ public class DropTargetContext implements Serializable * @exception InvalidDnDOperationException If a drop is not outstanding. */ public void dropComplete(boolean success) - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.dropComplete(success); } protected void acceptDrag(int dragOperation) - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.acceptDrag(dragOperation); } protected void rejectDrag() - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.rejectDrag(); } protected void acceptDrop(int dropOperation) - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.acceptDrop(dropOperation); } protected void rejectDrop() - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.rejectDrop(); } protected DataFlavor[] getCurrentDataFlavors() - throws NotImplementedException { - // FIXME: implement this + if (dtcp != null) + dtcp.getTransferDataFlavors(); return null; } @@ -182,9 +181,11 @@ public class DropTargetContext implements Serializable * @exception InvalidDnDOperationException If a drag is not outstanding. */ protected Transferable getTransferable() - throws InvalidDnDOperationException, NotImplementedException + throws InvalidDnDOperationException { - // FIXME: implement this + // FIXME: Implement this + if (dtcp != null) + return dtcp.getTransferable(); return null; } diff --git a/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java index 6cdc3a292be..89bf1778a71 100644 --- a/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java +++ b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java @@ -40,6 +40,7 @@ package java.awt.dnd; import java.awt.Point; import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; import java.util.List; /** @@ -114,8 +115,7 @@ public class DropTargetDragEvent extends DropTargetEvent public int getDropAction() { - return 0; - //return dropAction & ((DropTargetContext) source).getTargetActions(); + return dropAction & ((DropTargetContext) source).getTargetActions(); } public Point getLocation () @@ -137,4 +137,17 @@ public class DropTargetDragEvent extends DropTargetEvent { context.rejectDrag (); } + + /** + * TODO + * + * @return + * + * @since 1.5 + */ + public Transferable getTransferable() + { + // FIXME: Not implemented + return null; + } } // class DropTargetDragEvent diff --git a/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java index a745bd256f8..9754bb11ef5 100644 --- a/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java +++ b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java @@ -37,8 +37,6 @@ exception statement from your version. */ package java.awt.dnd; -import gnu.classpath.NotImplementedException; - import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; @@ -161,9 +159,8 @@ public class DropTargetDropEvent extends DropTargetEvent } public void dropComplete(boolean success) - throws NotImplementedException { - // FIXME: implement this + context.dropComplete(success); } public boolean isLocalTransfer() diff --git a/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java index 2fd9767e03d..4a75610bf61 100644 --- a/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java +++ b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java @@ -59,6 +59,7 @@ public class InvalidDnDOperationException extends IllegalStateException */ public InvalidDnDOperationException() { + super(); } /** diff --git a/libjava/classpath/java/awt/event/KeyEvent.java b/libjava/classpath/java/awt/event/KeyEvent.java index d4b93ba3e0b..42084d7333e 100644 --- a/libjava/classpath/java/awt/event/KeyEvent.java +++ b/libjava/classpath/java/awt/event/KeyEvent.java @@ -993,6 +993,27 @@ public class KeyEvent extends InputEvent public static final int VK_ALT_GRAPH = 65406; /** + * The 'begin' key VK_BEGIN + * + * @since 1.5 + */ + public static final int VK_BEGIN = 65368; + + /** + * The context-menu key VK_CONTEXT_MENU + * + * @since 1.5 + */ + public static final int VK_CONTEXT_MENU = 525; + + /** + * The 'Windows' key VK_WINDOWS + * + * @since 1.5 + */ + public static final int VK_WINDOWS = 524; + + /** * The virtual key VK_UNDEFINED. This is used for key typed events, which * do not have a virtual key. */ diff --git a/libjava/classpath/java/awt/font/FontRenderContext.java b/libjava/classpath/java/awt/font/FontRenderContext.java index 78564a647da..c50e5e5092a 100644 --- a/libjava/classpath/java/awt/font/FontRenderContext.java +++ b/libjava/classpath/java/awt/font/FontRenderContext.java @@ -83,7 +83,15 @@ public class FontRenderContext public boolean equals (FontRenderContext rhs) { - return (affineTransform.equals (rhs.getTransform ()) + if (rhs == null) + return false; + + if (affineTransform == null && rhs.affineTransform != null + || affineTransform != null && rhs.affineTransform == null) + return false; + + return ((affineTransform == rhs.affineTransform + || affineTransform.equals (rhs.getTransform ())) && isAntiAliased == rhs.isAntiAliased () && usesFractionalMetrics == rhs.usesFractionalMetrics ()); } diff --git a/libjava/classpath/java/awt/font/LineBreakMeasurer.java b/libjava/classpath/java/awt/font/LineBreakMeasurer.java index c2a6d45d9f5..816c7745c2b 100644 --- a/libjava/classpath/java/awt/font/LineBreakMeasurer.java +++ b/libjava/classpath/java/awt/font/LineBreakMeasurer.java @@ -41,57 +41,41 @@ package java.awt.font; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; -import java.awt.font.TextLayout; -import java.awt.font.FontRenderContext; import java.awt.Shape; public final class LineBreakMeasurer { private AttributedCharacterIterator text; private int position; - private FontRenderContext frc; - private TextLayout totalLayout; + private TextMeasurer tm; private int numChars; public LineBreakMeasurer(AttributedCharacterIterator text, BreakIterator breakIter, FontRenderContext frc) { - this.text = text; - this.frc = frc; - position = 0; - totalLayout = new TextLayout(text, frc); - numChars = totalLayout.getCharacterCount(); + this( text, frc ); } public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { this.text = text; - this.frc = frc; position = 0; - totalLayout = new TextLayout(text, frc); - numChars = totalLayout.getCharacterCount(); + numChars = text.getEndIndex(); + tm = new TextMeasurer( text, frc ); } public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) { - totalLayout = new TextLayout(newParagraph, frc); - if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() ) - throw new NullPointerException("Invalid deletePos:"+deletePos); - numChars = totalLayout.getCharacterCount(); - text = newParagraph; + tm.deleteChar( newParagraph, deletePos ); position = 0; } public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) { - totalLayout = new TextLayout(newParagraph, frc); - if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() ) - throw new NullPointerException("Invalid insertPos:"+insertPos); - numChars = totalLayout.getCharacterCount(); - text = newParagraph; + tm.insertChar( newParagraph, insertPos ); position = 0; } @@ -104,11 +88,9 @@ public final class LineBreakMeasurer boolean requireNextWord) { int next = nextOffset( wrappingWidth, offsetLimit, requireNextWord ); - AttributedCharacterIterator aci = (new AttributedString( text, - position, next ) - ).getIterator(); + TextLayout tl = tm.getLayout( position, next ); position = next; - return new TextLayout( aci, frc ); + return tl; } public int nextOffset(float wrappingWidth) @@ -119,69 +101,40 @@ public final class LineBreakMeasurer public int nextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) { - Shape s = totalLayout.getBlackBoxBounds( position, offsetLimit ); - double remainingLength = s.getBounds2D().getWidth(); + int guessOffset = tm.getLineBreakIndex(position, wrappingWidth); + if( offsetLimit > numChars ) + offsetLimit = numChars; - int guessOffset = (int)( ( (double)wrappingWidth / (double)remainingLength) - * ( (double)numChars - (double)position ) ); - guessOffset += position; if( guessOffset > offsetLimit ) - guessOffset = offsetLimit; - - s = totalLayout.getBlackBoxBounds( position, guessOffset ); - double guessLength = s.getBounds2D().getWidth(); - - boolean makeSmaller = ( guessLength > wrappingWidth ); - int inc = makeSmaller ? -1 : 1; - boolean keepGoing = true; - - do { - guessOffset = guessOffset + inc; - if( guessOffset <= position || guessOffset > offsetLimit ) - { - keepGoing = false; - } - else - { - s = totalLayout.getBlackBoxBounds( position, guessOffset ); - guessLength = s.getBounds2D().getWidth(); - if( makeSmaller && ( guessLength <= wrappingWidth) ) - keepGoing = false; - if( !makeSmaller && ( guessLength >= wrappingWidth) ) - keepGoing = false; - } + text.setIndex( offsetLimit ); + return offsetLimit; } - while( keepGoing ); - if( !makeSmaller ) - guessOffset--; + text.setIndex( guessOffset ); - if( guessOffset >= offsetLimit ) - return offsetLimit; + // If we're on a breaking character, return directly + if( Character.isWhitespace( text.current() ) ) + return guessOffset; - text.setIndex( guessOffset ); + // Otherwise jump forward or backward to the last such char. if( !requireNextWord ) - { - char c = text.previous(); - while( !Character.isWhitespace( c ) && c != '-' && - guessOffset > position ) - { - guessOffset--; - c = text.previous(); - } - } + while( !Character.isWhitespace( text.previous() ) && + guessOffset > position ) + guessOffset--; else + while( !Character.isWhitespace( text.next() ) && + guessOffset < offsetLimit ) + guessOffset++; + + if( guessOffset > offsetLimit ) { - char c = text.next(); - while( !Character.isWhitespace( c ) && c != '-' && - guessOffset < offsetLimit ) - { - guessOffset++; - c = text.next(); - } + text.setIndex( offsetLimit ); + return offsetLimit; } + text.setIndex( guessOffset ); + return guessOffset; } diff --git a/libjava/classpath/java/awt/font/TextLayout.java b/libjava/classpath/java/awt/font/TextLayout.java index 4f8c1c644c1..b1473f25564 100644 --- a/libjava/classpath/java/awt/font/TextLayout.java +++ b/libjava/classpath/java/awt/font/TextLayout.java @@ -43,13 +43,12 @@ import gnu.classpath.NotImplementedException; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Shape; -import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; +import java.text.CharacterIterator; import java.text.AttributedCharacterIterator; -import java.text.AttributedString; import java.text.Bidi; import java.util.Map; @@ -73,6 +72,12 @@ public final class TextLayout implements Cloneable private int[][] runIndices; /** + * Character indices. + * Fixt index is the glyphvector, second index is the (first) glyph. + */ + private int[][] charIndices; + + /** * Base directionality, determined from the first char. */ private boolean leftToRight; @@ -85,7 +90,7 @@ public final class TextLayout implements Cloneable /** * The default caret policy. */ - static TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); + public static final TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); /** * Constructs a TextLayout. @@ -139,6 +144,7 @@ public final class TextLayout implements Cloneable Font.LAYOUT_LEFT_TO_RIGHT : Font.LAYOUT_RIGHT_TO_LEFT ); } + setCharIndices(); } public TextLayout (String string, Map attributes, FontRenderContext frc) @@ -147,9 +153,97 @@ public final class TextLayout implements Cloneable } public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) - throws NotImplementedException { - throw new Error ("not implemented"); + // FIXME: Very rudimentary. + this(getText(text), getFont(text), frc); + } + + /** + * Package-private constructor to make a textlayout from an existing one. + * This is used by TextMeasurer for returning sub-layouts, and it + * saves a lot of time in not having to relayout the text. + */ + TextLayout(TextLayout t, int startIndex, int endIndex) + { + font = t.font; + frc = t.frc; + boundsCache = null; + lm = t.lm; + leftToRight = t.leftToRight; + + if( endIndex > t.getCharacterCount() ) + endIndex = t.getCharacterCount(); + string = t.string.substring( startIndex, endIndex ); + + int startingRun = t.charIndices[startIndex][0]; + int nRuns = 1 + t.charIndices[endIndex - 1][0] - startingRun; + runIndices = new int[ nRuns ][2]; + + runs = new GlyphVector[ nRuns ]; + for( int i = 0; i < nRuns; i++ ) + { + GlyphVector run = t.runs[ i + startingRun ]; + // Copy only the relevant parts of the first and last runs. + int beginGlyphIndex = (i > 0) ? 0 : t.charIndices[startIndex][1]; + int numEntries = ( i < nRuns - 1) ? run.getNumGlyphs() : + 1 + t.charIndices[endIndex - 1][1] - beginGlyphIndex; + + int[] codes = run.getGlyphCodes(beginGlyphIndex, numEntries, null); + runs[ i ] = font.createGlyphVector( frc, codes ); + runIndices[ i ][0] = t.runIndices[i + startingRun][0] - startIndex; + runIndices[ i ][1] = t.runIndices[i + startingRun][1] - startIndex; + } + runIndices[ nRuns - 1 ][1] = endIndex - 1; + + setCharIndices(); + determineWhiteSpace(); + } + + private void setCharIndices() + { + charIndices = new int[ getCharacterCount() ][2]; + int i = 0; + int currentChar = 0; + for(int run = 0; run < runs.length; run++) + { + currentChar = -1; + for( int gi = 0; gi < runs[ run ].getNumGlyphs(); gi++) + { + if( runs[ run ].getGlyphCharIndex( gi ) != currentChar ) + { + charIndices[ i ][0] = run; + charIndices[ i ][1] = gi; + currentChar = runs[ run ].getGlyphCharIndex( gi ); + i++; + } + } + } + } + + private static String getText(AttributedCharacterIterator iter) + { + StringBuffer sb = new StringBuffer(); + int idx = iter.getIndex(); + for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) + sb.append(c); + iter.setIndex( idx ); + return sb.toString(); + } + + private static Font getFont(AttributedCharacterIterator iter) + { + Font f = (Font)iter.getAttribute(TextAttribute.FONT); + if( f == null ) + { + int size; + Float i = (Float)iter.getAttribute(TextAttribute.SIZE); + if( i != null ) + size = (int)i.floatValue(); + else + size = 14; + f = new Font("Dialog", Font.PLAIN, size ); + } + return f; } /** @@ -179,10 +273,14 @@ public final class TextLayout implements Cloneable gotDirection = true; break; } + determineWhiteSpace(); + } + private void determineWhiteSpace() + { // Determine if there's whitespace in the thing. // Ignore trailing chars. - i = string.length() - 1; + int i = string.length() - 1; hasWhitespace = false; while( i >= 0 && Character.isWhitespace( string.charAt(i) ) ) i--; @@ -251,56 +349,42 @@ public final class TextLayout implements Cloneable public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint) { - if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount()) return new Rectangle2D.Float(); GeneralPath gp = new GeneralPath(); - int i = 0; // run index - double advance = 0; - - // go to first run - while( runIndices[i + 1][1] < firstEndpoint ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - } + + int ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; - int j = 0; // index into the run. - if( runIndices[i][1] - runIndices[i][0] > 1 ) + double advance = 0; + + for( int i = 0; i < ri; i++ ) + advance += runs[i].getLogicalBounds().getWidth(); + + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) { - while( runs[i].getGlyphCharIndex( j + 1 ) < - (firstEndpoint - runIndices[i][0] ) )j++; - } - - gp.append(runs[i].getGlyphVisualBounds( j ), false); - boolean keepGoing = true;; + int dg; + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = runs[i].getNumGlyphs() - 1; - do - { - while( j < runs[i].getNumGlyphs() && - runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < - secondEndpoint ) + for( int j = 0; j <= dg; j++ ) { Rectangle2D r2 = (runs[i].getGlyphVisualBounds( j )). getBounds2D(); Point2D p = runs[i].getGlyphPosition( j ); - r2.setRect( advance + p.getX(), r2.getY(), + r2.setRect( advance + r2.getX(), r2.getY(), r2.getWidth(), r2.getHeight() ); gp.append(r2, false); - j++; } - if( j >= runs[i].getNumGlyphs() ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - j = 0; - } - else - keepGoing = false; + advance += runs[i].getLogicalBounds().getWidth(); } - while( keepGoing ); - return gp; } @@ -384,55 +468,42 @@ public final class TextLayout implements Cloneable public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint, Rectangle2D bounds) { - if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount()) return new Rectangle2D.Float(); - int i = 0; // run index - double advance = 0; + Rectangle2D r = null; + int ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; - // go to first run - if( i > 0 ) - while( runIndices[i + 1][1] < firstEndpoint ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - } + double advance = 0; + + for( int i = 0; i < ri; i++ ) + advance += runs[i].getLogicalBounds().getWidth(); - int j = 0; // index into the run. - if( runIndices[i][1] - runIndices[i][0] > 1 ) + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) { - while( runs[i].getGlyphCharIndex( j + 1 ) < - (firstEndpoint - runIndices[i][0] ) )j++; - } - - Rectangle2D r = (runs[i].getGlyphLogicalBounds( j )).getBounds2D(); - boolean keepGoing = true;; + int dg; // last index in this run to use. + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = runs[i].getNumGlyphs() - 1; - do - { - while( j < runs[i].getNumGlyphs() && - runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < - secondEndpoint ) + for(; gi <= dg; gi++ ) { - Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( j )). + Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( gi )). getBounds2D(); - Point2D p = runs[i].getGlyphPosition( j ); - r2.setRect( advance + p.getX(), r2.getY(), - r2.getWidth(), r2.getHeight() ); - r = r.createUnion( r2 ); - j++; + if( r == null ) + r = r2; + else + r = r.createUnion(r2); } + gi = 0; // reset glyph index into run for next run. - if( j >= runs[i].getNumGlyphs() ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - j = 0; - } - else - keepGoing = false; + advance += runs[i].getLogicalBounds().getWidth(); } - while( keepGoing ); return r; } @@ -593,8 +664,9 @@ public final class TextLayout implements Cloneable } public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds) + throws NotImplementedException { - return hitTestChar( x, y, getBounds() ); + throw new Error ("not implemented"); } public boolean isLeftToRight () diff --git a/libjava/classpath/java/awt/font/TextMeasurer.java b/libjava/classpath/java/awt/font/TextMeasurer.java index 18c286c57c1..00cab8a878d 100644 --- a/libjava/classpath/java/awt/font/TextMeasurer.java +++ b/libjava/classpath/java/awt/font/TextMeasurer.java @@ -1,5 +1,5 @@ /* TextMeasurer.java - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,67 +38,154 @@ exception statement from your version. */ package java.awt.font; -import gnu.classpath.NotImplementedException; - import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.awt.Shape; /** - * @author Michael Koch + * TextMeasurer is a small utility class for measuring the length of laid-out + * text objects. + * + * @author Sven de Marothy * @since 1.3 */ public final class TextMeasurer implements Cloneable { - private AttributedCharacterIterator ci; + private AttributedCharacterIterator text; private FontRenderContext frc; - + private TextLayout totalLayout; + private int numChars; + + /** + * Creates a TextMeasurer from a given text in the form of an + * <code>AttributedCharacterIterator</code> and a + * <code>FontRenderContext</code>. + */ public TextMeasurer (AttributedCharacterIterator text, FontRenderContext frc) { - this.ci = text; + this.text = text; this.frc = frc; + totalLayout = new TextLayout( text, frc ); + numChars = totalLayout.getCharacterCount(); } + /** + * Clones the TextMeasurer object + */ protected Object clone () { - try - { - return super.clone (); - } - catch (CloneNotSupportedException e) - { - // This may never occur - throw new InternalError (); - } + return new TextMeasurer( text, frc ); } + /** + * Update the text if a character is deleted at the position deletePos + * @param newParagraph - the updated paragraph. + * @param deletePos - the deletion position + */ public void deleteChar (AttributedCharacterIterator newParagraph, int deletePos) - throws NotImplementedException { - throw new Error ("not implemented"); + totalLayout = new TextLayout(newParagraph, frc); + if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid deletePos:"+deletePos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; + } + + /** + * Update the text if a character is inserted at the position insertPos + * @param newParagraph - the updated paragraph. + * @param insertPos - the insertion position + */ + public void insertChar (AttributedCharacterIterator newParagraph, + int insertPos) + { + totalLayout = new TextLayout(newParagraph, frc); + if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid insertPos:"+insertPos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; } + /*** + * Returns the total advance between two positions in the paragraph. + * Characters from start to limit-1 (inclusive) are included in this count. + * + * @param start - the starting character index. + * @param limit - the limiting index. + */ public float getAdvanceBetween (int start, int limit) - throws NotImplementedException { - throw new Error ("not implemented"); + Shape s = totalLayout.getLogicalHighlightShape( start, limit ); + return (float)s.getBounds2D().getWidth(); } + /** + * Returns a <code>TextLayout</code> object corresponding to the characters + * from text to limit. + * @param start - the starting character index. + * @param limit - the limiting index. + */ public TextLayout getLayout (int start, int limit) - throws NotImplementedException { - throw new Error ("not implemented"); + if( start >= limit ) + throw new IllegalArgumentException("Start position must be < limit."); + return new TextLayout( totalLayout, start, limit ); } + /** + * Returns the line-break index from a given starting index and a maximum + * advance. The index returned is the first character outside the given + * advance (or the limit of the string, if all remaining characters fit.) + * + * @param start - the starting index. + * @param maxAdvance - the maximum advance allowed. + * @return the index of the first character beyond maxAdvance, or the + * index of the last character + 1. + */ public int getLineBreakIndex (int start, float maxAdvance) - throws NotImplementedException - { - throw new Error ("not implemented"); - } + { + if( start < 0 ) + throw new IllegalArgumentException("Start parameter must be > 0."); + + double remainingLength = getAdvanceBetween( start, numChars ); + + int guessOffset = (int)( ( (double)maxAdvance / (double)remainingLength) + * ( (double)numChars - (double)start ) ); + guessOffset += start; + if( guessOffset > numChars ) + guessOffset = numChars; + + double guessLength = getAdvanceBetween( start, guessOffset ); + boolean makeSmaller = ( guessLength > maxAdvance ); + int inc = makeSmaller ? -1 : 1; + boolean keepGoing = true; + + do + { + guessOffset = guessOffset + inc; + if( guessOffset <= start || guessOffset > numChars ) + { + keepGoing = false; + } + else + { + guessLength = getAdvanceBetween( start, guessOffset ); + if( makeSmaller && ( guessLength <= maxAdvance) ) + keepGoing = false; + if( !makeSmaller && ( guessLength >= maxAdvance) ) + keepGoing = false; + } + } + while( keepGoing ); - public void insertChar (AttributedCharacterIterator newParagraph, - int insertPos) - throws NotImplementedException - { - throw new Error ("not implemented"); + // Return first index that doesn't fit. + if( !makeSmaller ) + guessOffset--; + + if( guessOffset > numChars ) + return numChars; + + return guessOffset; } } diff --git a/libjava/classpath/java/awt/geom/GeneralPath.java b/libjava/classpath/java/awt/geom/GeneralPath.java index 123833b118b..e0ca8e18357 100644 --- a/libjava/classpath/java/awt/geom/GeneralPath.java +++ b/libjava/classpath/java/awt/geom/GeneralPath.java @@ -65,8 +65,8 @@ import java.awt.Shape; * ’up’ * direction, one in the ’down’ direction) Point <b>B</b> in * the image is inside (one intersection ’down’) - * Point <b>C</b> in the image is outside (two intersections - * ’down’) + * Point <b>C</b> in the image is inside (two intersections in the + * ’down’ direction) * * @see Line2D * @see CubicCurve2D @@ -247,10 +247,12 @@ public final class GeneralPath implements Shape, Cloneable /** * Closes the current subpath by drawing a line - * back to the point of the last moveTo. + * back to the point of the last moveTo, unless the path is already closed. */ public void closePath() { + if (index >= 1 && types[index - 1] == PathIterator.SEG_CLOSE) + return; ensureSize(index + 1); types[index] = PathIterator.SEG_CLOSE; xpoints[index] = xpoints[subpath]; diff --git a/libjava/classpath/java/awt/image/BandedSampleModel.java b/libjava/classpath/java/awt/image/BandedSampleModel.java index 24d315a1c35..afe62bdc4bd 100644 --- a/libjava/classpath/java/awt/image/BandedSampleModel.java +++ b/libjava/classpath/java/awt/image/BandedSampleModel.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2004, 2005, Free Software Foundation +/* Copyright (C) 2004, 2005, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -36,10 +36,11 @@ exception statement from your version. */ package java.awt.image; +import gnu.java.awt.Buffers; + /** - * MultiPixelPackedSampleModel provides a single band model that supports - * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit - * per data element. + * A sample model that reads each sample value from a separate band in the + * {@link DataBuffer}. * * @author Jerry Quinn (jlquinn@optonline.net) */ @@ -61,17 +62,61 @@ public final class BandedSampleModel extends ComponentSampleModel return result; } + /** + * Creates a new <code>BandedSampleModel</code>. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numBands the number of bands. + */ public BandedSampleModel(int dataType, int w, int h, int numBands) { this(dataType, w, h, w, createBankArray(numBands), new int[numBands]); } + /** + * Creates a new <code>BandedSampleModel</code>. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param scanlineStride the number of data elements from a pixel in one + * row to the corresponding pixel in the next row. + * @param bankIndices the bank indices. + * @param bandOffsets the band offsets. + */ public BandedSampleModel(int dataType, int w, int h, int scanlineStride, int[] bankIndices, int[] bandOffsets) { super(dataType, w, h, 1, scanlineStride, bankIndices, bandOffsets); } + + /** + * Creates a new data buffer that is compatible with this sample model. + * + * @return The new data buffer. + */ + public DataBuffer createDataBuffer() + { + int size = scanlineStride * height; + return Buffers.createBuffer(getDataType(), size, numBanks); + } + /** + * Creates a new <code>SampleModel</code> that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels, must be greater than zero). + * @param h the height (in pixels, must be greater than zero). + * + * @return The new sample model. + * + * @throws IllegalArgumentException if <code>w</code> or <code>h</code> is + * not greater than zero. + * @throws IllegalArgumentException if <code>w * h</code> exceeds + * <code>Integer.MAX_VALUE</code>. + */ public SampleModel createCompatibleSampleModel(int w, int h) { // NOTE: blackdown 1.4.1 sets all offsets to 0. Sun's 1.4.2 docs @@ -80,32 +125,32 @@ public final class BandedSampleModel extends ComponentSampleModel // Compress offsets so minimum is 0, others w*scanlineStride int[] newoffsets = new int[bandOffsets.length]; int[] order = new int[bandOffsets.length]; - for (int i=0; i < bandOffsets.length; i++) + for (int i = 0; i < bandOffsets.length; i++) order[i] = i; // FIXME: This is N^2, but not a big issue, unless there's a lot of // bands... - for (int i=0; i < bandOffsets.length; i++) - for (int j=i+1; j < bandOffsets.length; i++) - if (bankIndices[order[i]] > bankIndices[order[j]] - || (bankIndices[order[i]] == bankIndices[order[j]] - && bandOffsets[order[i]] > bandOffsets[order[j]])) - { - int t = order[i]; order[i] = order[j]; order[j] = t; - } + for (int i = 0; i < bandOffsets.length; i++) + for (int j = i + 1; j < bandOffsets.length; j++) + if (bankIndices[order[i]] > bankIndices[order[j]] + || (bankIndices[order[i]] == bankIndices[order[j]] + && bandOffsets[order[i]] > bandOffsets[order[j]])) + { + int t = order[i]; order[i] = order[j]; order[j] = t; + } int bank = 0; int offset = 0; - for (int i=0; i < bandOffsets.length; i++) + for (int i = 0; i < bandOffsets.length; i++) { - if (bankIndices[order[i]] != bank) - { - bank = bankIndices[order[i]]; - offset = 0; - } - newoffsets[order[i]] = offset; - offset += w * scanlineStride; + if (bankIndices[order[i]] != bank) + { + bank = bankIndices[order[i]]; + offset = 0; + } + newoffsets[order[i]] = offset; + offset += w * scanlineStride; } - return new BandedSampleModel(dataType, w, h, scanlineStride, bankIndices, newoffsets); + return new BandedSampleModel(dataType, w, h, w, bankIndices, newoffsets); } @@ -117,7 +162,7 @@ public final class BandedSampleModel extends ComponentSampleModel +" many bands"); int[] newoff = new int[bands.length]; int[] newbanks = new int[bands.length]; - for (int i=0; i < bands.length; i++) + for (int i = 0; i < bands.length; i++) { int b = bands[i]; newoff[i] = bandOffsets[b]; @@ -134,57 +179,64 @@ public final class BandedSampleModel extends ComponentSampleModel * Extracts the pixel at x, y from data and stores samples into the array * obj. If obj is null, a new array of getTransferType() is created. * - * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. - * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. - * @param obj The primitive array to store the pixels into or null to force creation. + * @param x The x-coordinate of the pixel rectangle to store in + * <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in + * <code>obj</code>. + * @param obj The primitive array to store the pixels into or null to force + * creation. * @param data The DataBuffer that is the source of the pixel data. * @return The primitive array containing the pixel data. - * @see java.awt.image.SampleModel#getDataElements(int, int, java.lang.Object, java.awt.image.DataBuffer) + * @see java.awt.image.SampleModel#getDataElements(int, int, + * java.lang.Object, java.awt.image.DataBuffer) */ - public Object getDataElements(int x, int y, Object obj, - DataBuffer data) + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); int pixel = getSample(x, y, 0, data); switch (getTransferType()) { case DataBuffer.TYPE_BYTE: { - byte[] b = (byte[])obj; + byte[] b = (byte[]) obj; if (b == null) b = new byte[numBands]; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) b[i] = (byte)getSample(x, y, i, data); return b; } case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: { - short[] b = (short[])obj; + short[] b = (short[]) obj; if (b == null) b = new short[numBands]; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) b[i] = (short)getSample(x, y, i, data); return b; } case DataBuffer.TYPE_INT: { - int[] b = (int[])obj; + int[] b = (int[]) obj; if (b == null) b = new int[numBands]; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) b[i] = getSample(x, y, i, data); return b; } case DataBuffer.TYPE_FLOAT: { - float[] b = (float[])obj; + float[] b = (float[]) obj; if (b == null) b = new float[numBands]; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) b[i] = getSampleFloat(x, y, i, data); return b; } case DataBuffer.TYPE_DOUBLE: { - double[] b = (double[])obj; - if (b == null) b = new double[numBands]; - for (int i=0; i < numBands; i++) + double[] b = (double[]) obj; + if (b == null) + b = new double[numBands]; + for (int i = 0; i < numBands; i++) b[i] = getSample(x, y, i, data); return b; } @@ -195,10 +247,27 @@ public final class BandedSampleModel extends ComponentSampleModel } } + /** + * Returns all the samples for the pixel at location <code>(x, y)</code> + * stored in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param iArray an array that will be populated with the sample values and + * returned as the result. The size of this array should be equal to the + * number of bands in the model. If the array is <code>null</code>, a new + * array is created. + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The samples for the specified pixel. + * + * @see #setPixel(int, int, int[], DataBuffer) + */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { - if (iArray == null) iArray = new int[numBands]; - for (int i=0; i < numBands; i++) + if (iArray == null) + iArray = new int[numBands]; + for (int i = 0; i < numBands; i++) iArray[i] = getSample(x, y, i, data); return iArray; @@ -228,7 +297,11 @@ public final class BandedSampleModel extends ComponentSampleModel public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { - if (iArray == null) iArray = new int[w*h*numBands]; + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + if (iArray == null) + iArray = new int[w * h * numBands]; int outOffset = 0; int maxX = x + w; int maxY = y + h; @@ -247,18 +320,64 @@ public final class BandedSampleModel extends ComponentSampleModel return iArray; } + /** + * Returns a sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int getSample(int x, int y, int b, DataBuffer data) { int offset = bandOffsets[b] + y * scanlineStride + x; return data.getElem(bankIndices[b], offset); } + /** + * Returns a sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + * + * @see #getSample(int, int, int, DataBuffer) + */ public float getSampleFloat(int x, int y, int b, DataBuffer data) { int offset = bandOffsets[b] + y * scanlineStride + x; return data.getElemFloat(bankIndices[b], offset); } + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + * + * @see #getSample(int, int, int, DataBuffer) + */ public double getSampleDouble(int x, int y, int b, DataBuffer data) { int offset = bandOffsets[b] + y * scanlineStride + x; @@ -288,7 +407,11 @@ public final class BandedSampleModel extends ComponentSampleModel public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { - if (iArray == null) iArray = new int[w*h]; + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + if (iArray == null) + iArray = new int[w * h]; int outOffset = 0; int maxX = x + w; int maxY = y + h; @@ -304,7 +427,6 @@ public final class BandedSampleModel extends ComponentSampleModel return iArray; } - /** * Set the pixel at x, y to the value in the first element of the primitive * array obj. @@ -338,7 +460,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferByte out = (DataBufferByte) data; byte[] in = (byte[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -346,7 +468,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferShort out = (DataBufferShort) data; short[] in = (short[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -354,7 +476,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferUShort out = (DataBufferUShort) data; short[] in = (short[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -362,7 +484,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferInt out = (DataBufferInt) data; int[] in = (int[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -370,7 +492,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferFloat out = (DataBufferFloat) data; float[] in = (float[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -378,7 +500,7 @@ public final class BandedSampleModel extends ComponentSampleModel { DataBufferDouble out = (DataBufferDouble) data; double[] in = (double[]) obj; - for (int i=0; i < numBands; i++) + for (int i = 0; i < numBands; i++) out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; return; } @@ -388,26 +510,54 @@ public final class BandedSampleModel extends ComponentSampleModel } catch (ArrayIndexOutOfBoundsException aioobe) { - String msg = "While writing data elements" + - ", x="+x+", y="+y+ - ", width="+width+", height="+height+ - ", scanlineStride="+scanlineStride+ - ", offset="+offset+ - ", data.getSize()="+data.getSize()+ - ", data.getOffset()="+data.getOffset()+ - ": " + - aioobe; + String msg = "While writing data elements" + + ", x=" + x + ", y=" + y + + ", width=" + width + ", height=" + height + + ", scanlineStride=" + scanlineStride + + ", offset=" + offset + + ", data.getSize()=" + data.getSize() + + ", data.getOffset()=" + data.getOffset() + + ": " + aioobe; throw new ArrayIndexOutOfBoundsException(msg); } } + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { - for (int b=0; b < numBands; b++) + for (int b = 0; b < numBands; b++) data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, iArray[b]); } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { @@ -417,7 +567,7 @@ public final class BandedSampleModel extends ComponentSampleModel for (int ww = 0; ww < w; ww++) { int offset = y * scanlineStride + (x + ww); - for (int b=0; b < numBands; b++) + for (int b = 0; b < numBands; b++) data.setElem(bankIndices[b], bandOffsets[b] + offset, iArray[inOffset++]); } @@ -425,24 +575,83 @@ public final class BandedSampleModel extends ComponentSampleModel } } + /** + * Sets the sample value for band <code>b</code> of the pixel at location + * <code>(x, y)</code> in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param b the band index. + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @see #getSample(int, int, int, DataBuffer) + */ public void setSample(int x, int y, int b, int s, DataBuffer data) { data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, float s, DataBuffer data) { - data.setElemFloat(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + data.setElemFloat(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + s); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, double s, DataBuffer data) { - data.setElemDouble(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + data.setElemDouble(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + s); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param iArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); int inOffset = 0; switch (getTransferType()) @@ -537,9 +746,10 @@ public final class BandedSampleModel extends ComponentSampleModel result.append(getClass().getName()); result.append("["); result.append("scanlineStride=").append(scanlineStride); - for(int i=0; i < bitMasks.length; i+=1) + for(int i = 0; i < bitMasks.length; i+=1) { - result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + result.append(", mask[").append(i).append("]=0x").append( + Integer.toHexString(bitMasks[i])); } result.append("]"); diff --git a/libjava/classpath/java/awt/image/BufferedImage.java b/libjava/classpath/java/awt/image/BufferedImage.java index 77b8d6cc174..76848db0833 100644 --- a/libjava/classpath/java/awt/image/BufferedImage.java +++ b/libjava/classpath/java/awt/image/BufferedImage.java @@ -100,11 +100,33 @@ public class BufferedImage extends Image Vector observers; /** - * Creates a new buffered image. + * Creates a new <code>BufferedImage</code> with the specified width, height + * and type. Valid <code>type</code> values are: * - * @param w the width. - * @param h the height. - * @param type the image type (see the constants defined by this class). + * <ul> + * <li>{@link #TYPE_INT_RGB}</li> + * <li>{@link #TYPE_INT_ARGB}</li> + * <li>{@link #TYPE_INT_ARGB_PRE}</li> + * <li>{@link #TYPE_INT_BGR}</li> + * <li>{@link #TYPE_3BYTE_BGR}</li> + * <li>{@link #TYPE_4BYTE_ABGR}</li> + * <li>{@link #TYPE_4BYTE_ABGR_PRE}</li> + * <li>{@link #TYPE_USHORT_565_RGB}</li> + * <li>{@link #TYPE_USHORT_555_RGB}</li> + * <li>{@link #TYPE_BYTE_GRAY}</li> + * <li>{@link #TYPE_USHORT_GRAY}</li> + * <li>{@link #TYPE_BYTE_BINARY}</li> + * <li>{@link #TYPE_BYTE_INDEXED}</li> + * </ul> + * + * @param w the width (must be > 0). + * @param h the height (must be > 0). + * @param type the image type (see the list of valid types above). + * + * @throws IllegalArgumentException if <code>w</code> or <code>h</code> is + * less than or equal to zero. + * @throws IllegalArgumentException if <code>type</code> is not one of the + * specified values. */ public BufferedImage(int w, int h, int type) { @@ -181,13 +203,15 @@ public class BufferedImage extends Image case TYPE_4BYTE_ABGR_PRE: bits = bits4; break; - case TYPE_BYTE_GRAY: - bits = bits1byte; - break; - case TYPE_USHORT_GRAY: - bits = bits1ushort; - dataType = DataBuffer.TYPE_USHORT; - break; + case TYPE_BYTE_GRAY: + bits = bits1byte; + cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); + break; + case TYPE_USHORT_GRAY: + bits = bits1ushort; + cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); + dataType = DataBuffer.TYPE_USHORT; + break; } cm = new ComponentColorModel(cs, bits, alpha, premultiplied, alpha ? @@ -203,6 +227,8 @@ public class BufferedImage extends Image String msg2 = "type not implemented yet"; throw new UnsupportedOperationException(msg2); // FIXME: build color-cube and create color model + default: + throw new IllegalArgumentException("Unknown image type " + type); } init(cm, @@ -504,7 +530,10 @@ public class BufferedImage extends Image int[] pixels = getRGB(x, y, width, height, (int[])null, offset, stride); - ColorModel model = getColorModel(); + // We already convert the color to RGB in the getRGB call, so + // we pass a simple RGB color model to the consumers. + ColorModel model = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, + 0xff000000); consumers.add(ic); diff --git a/libjava/classpath/java/awt/image/BufferedImageOp.java b/libjava/classpath/java/awt/image/BufferedImageOp.java index 2ecbec056a0..f6a24c976ab 100644 --- a/libjava/classpath/java/awt/image/BufferedImageOp.java +++ b/libjava/classpath/java/awt/image/BufferedImageOp.java @@ -1,5 +1,5 @@ /* BufferedImageOp.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,13 +43,65 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** - * NEEDS DOCUMENTATION + * An operation that is performed on one <code>BufferedImage</code> (the + * source) producing a new <code>BufferedImage</code> (the destination). */ public interface BufferedImageOp { + /** + * Performs an operation on the source image, returning the result in a + * <code>BufferedImage</code>. If <code>dest</code> is <code>null</code>, a + * new <code>BufferedImage</code> will be created by calling the + * {@link #createCompatibleDestImage} method. If <code>dest</code> + * is not <code>null</code>, the result is written to <code>dest</code> then + * returned (this avoids creating a new <code>BufferedImage</code> each + * time this method is called). + * + * @param src the source image. + * @param dst the destination image (<code>null</code> permitted). + * + * @return The filterd image. + */ BufferedImage filter(BufferedImage src, BufferedImage dst); + + /** + * Returns the bounds of the destination image on the basis of this + * <code>BufferedImageOp</code> being applied to the specified source image. + * + * @param src the source image. + * + * @return The destination bounds. + */ Rectangle2D getBounds2D(BufferedImage src); + + /** + * Returns a new <code>BufferedImage</code> that can be used by this + * <code>BufferedImageOp</code> as the destination image when filtering + * the specified source image. + * + * @param src the source image. + * @param dstCM the color model for the destination image. + * + * @return A new image that can be used as the destination image. + */ BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM); + + /** + * Returns the point on the destination image that corresponds to the given + * point on the source image. + * + * @param src the source point. + * @param dst the destination point (<code>null</code> permitted). + * + * @return The destination point. + */ Point2D getPoint2D(Point2D src, Point2D dst); + + /** + * Returns the rendering hints for this operation. + * + * @return The rendering hints. + */ RenderingHints getRenderingHints(); -} // interface BufferedImageOp + +} diff --git a/libjava/classpath/java/awt/image/ByteLookupTable.java b/libjava/classpath/java/awt/image/ByteLookupTable.java index df02d0a1bf7..ecc0023aff6 100644 --- a/libjava/classpath/java/awt/image/ByteLookupTable.java +++ b/libjava/classpath/java/awt/image/ByteLookupTable.java @@ -1,5 +1,5 @@ /* ByteLookupTable.java -- Java class for a pixel translation table. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -60,14 +60,20 @@ public class ByteLookupTable extends LookupTable * components. * * @param offset Offset to be subtracted. - * @param data Array of lookup tables. + * @param data Array of lookup tables (<code>null</code> not permitted). * @exception IllegalArgumentException if offset < 0 or data.length < 1. */ public ByteLookupTable(int offset, byte[][] data) throws IllegalArgumentException { super(offset, data.length); - this.data = data; + + // tests show that Sun's implementation creates a new array to store the + // references from the incoming 'data' array - not sure why, but we'll + // match that behaviour just in case it matters... + this.data = new byte[data.length][]; + for (int i = 0; i < data.length; i++) + this.data[i] = data[i]; } /** @@ -77,13 +83,16 @@ public class ByteLookupTable extends LookupTable * table. The same table is applied to all pixel components. * * @param offset Offset to be subtracted. - * @param data Lookup table for all components. + * @param data Lookup table for all components (<code>null</code> not + * permitted). * @exception IllegalArgumentException if offset < 0. */ public ByteLookupTable(int offset, byte[] data) throws IllegalArgumentException { super(offset, 1); + if (data == null) + throw new NullPointerException("Null 'data' argument."); this.data = new byte[][] {data}; } diff --git a/libjava/classpath/java/awt/image/ColorConvertOp.java b/libjava/classpath/java/awt/image/ColorConvertOp.java index 18609e0c4b0..1f85a5ecd99 100644 --- a/libjava/classpath/java/awt/image/ColorConvertOp.java +++ b/libjava/classpath/java/awt/image/ColorConvertOp.java @@ -1,5 +1,5 @@ -/* ColorModel.java -- - Copyright (C) 2004 Free Software Foundation +/* ColorConvertOp.java -- + Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -177,8 +177,7 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp ColorModel scm = src.getColorModel(); for (int i = 0; i < spaces.length; i++) { - ColorModel cm = scm.cloneColorModel(spaces[i]); - BufferedImage tmp = createCompatibleDestImage(src, cm); + BufferedImage tmp = createCompatibleDestImage(src, scm); copyimage(src, tmp); src = tmp; } @@ -189,6 +188,7 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp // Apply final conversion copyimage(src, dst); + return dst; } @@ -287,7 +287,12 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp private void copyimage(BufferedImage src, BufferedImage dst) { Graphics2D gg = dst.createGraphics(); - gg.setRenderingHints(hints); + + // If no hints are set there is no need to call + // setRenderingHints on the Graphics2D object. + if (hints != null) + gg.setRenderingHints(hints); + gg.drawImage(src, 0, 0, null); gg.dispose(); } diff --git a/libjava/classpath/java/awt/image/ColorModel.java b/libjava/classpath/java/awt/image/ColorModel.java index e2f5378b4da..9e559db37d8 100644 --- a/libjava/classpath/java/awt/image/ColorModel.java +++ b/libjava/classpath/java/awt/image/ColorModel.java @@ -1,5 +1,5 @@ /* ColorModel.java -- - Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -43,7 +43,6 @@ import gnu.java.awt.Buffers; import java.awt.Point; import java.awt.Transparency; import java.awt.color.ColorSpace; -import java.lang.reflect.Constructor; import java.util.Arrays; /** @@ -163,32 +162,6 @@ public abstract class ColorModel implements Transparency this.transparency = transparency; this.transferType = transferType; } - - // This is a hook for ColorConvertOp to create a colormodel with - // a new colorspace - ColorModel cloneColorModel(ColorSpace cspace) - { - Class cls = this.getClass(); - ColorModel cm; - try { - // This constructor will exist. - Constructor ctor = - cls.getConstructor(new Class[]{int.class, int[].class, - ColorSpace.class, boolean.class, - boolean.class, int.class, int.class}); - cm = (ColorModel)ctor. - newInstance(new Object[]{new Integer(pixel_bits), - bits, cspace, Boolean.valueOf(hasAlpha), - Boolean.valueOf(isAlphaPremultiplied), - new Integer(transparency), - new Integer(transferType)}); - } - catch (Exception e) - { - throw new IllegalArgumentException(); - } - return cm; - } public void finalize() { diff --git a/libjava/classpath/java/awt/image/ComponentSampleModel.java b/libjava/classpath/java/awt/image/ComponentSampleModel.java index b4e9450b060..bccabbbcadb 100644 --- a/libjava/classpath/java/awt/image/ComponentSampleModel.java +++ b/libjava/classpath/java/awt/image/ComponentSampleModel.java @@ -272,9 +272,7 @@ public class ComponentSampleModel extends SampleModel // Maybe this value should be precalculated in the constructor? int highestOffset = 0; for (int b = 0; b < numBands; b++) - { - highestOffset = Math.max(highestOffset, bandOffsets[b]); - } + highestOffset = Math.max(highestOffset, bandOffsets[b]); int size = pixelStride * (width - 1) + scanlineStride * (height - 1) + highestOffset + 1; @@ -678,6 +676,9 @@ public class ComponentSampleModel extends SampleModel */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { + if (x < 0 || x >= width || y < 0 || y >= height) + throw new ArrayIndexOutOfBoundsException("Pixel (" + x + ", " + y + + ") is out of bounds."); int offset = pixelStride * x + scanlineStride * y; if (iArray == null) iArray = new int[numBands]; @@ -736,10 +737,16 @@ public class ComponentSampleModel extends SampleModel * * @return The sample value. * + * @throws ArrayIndexOutOfBoundsException if <code>(x, y)</code> is outside + * the bounds <code>[0, 0, width, height]</code>. + * * @see #setSample(int, int, int, int, DataBuffer) */ public int getSample(int x, int y, int b, DataBuffer data) { + if (x < 0 || x >= width || y < 0 || y >= height) + throw new ArrayIndexOutOfBoundsException("Sample (" + x + ", " + y + + ") is out of bounds."); return data.getElem(bankIndices[b], getOffset(x, y, b)); } diff --git a/libjava/classpath/java/awt/image/ConvolveOp.java b/libjava/classpath/java/awt/image/ConvolveOp.java index 1f73f75b233..ffb834874fa 100644 --- a/libjava/classpath/java/awt/image/ConvolveOp.java +++ b/libjava/classpath/java/awt/image/ConvolveOp.java @@ -1,5 +1,5 @@ /* ConvolveOp.java -- - Copyright (C) 2004, 2005 Free Software Foundation -- ConvolveOp + Copyright (C) 2004, 2005, 2006, Free Software Foundation -- ConvolveOp This file is part of GNU Classpath. @@ -42,7 +42,6 @@ import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.util.Arrays; /** * Convolution filter. @@ -190,112 +189,101 @@ public class ConvolveOp implements BufferedImageOp, RasterOp * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, * java.awt.image.WritableRaster) */ - public final WritableRaster filter(Raster src, WritableRaster dest) { + public final WritableRaster filter(Raster src, WritableRaster dest) + { if (src == dest) - throw new IllegalArgumentException(); - if (src.getWidth() < kernel.getWidth() || - src.getHeight() < kernel.getHeight()) - throw new ImagingOpException(null); - + throw new IllegalArgumentException("src == dest is not allowed."); + if (kernel.getWidth() > src.getWidth() + || kernel.getHeight() > src.getHeight()) + throw new ImagingOpException("The kernel is too large."); if (dest == null) dest = createCompatibleDestRaster(src); - else if (src.numBands != dest.numBands) - throw new ImagingOpException(null); + else if (src.getNumBands() != dest.getNumBands()) + throw new ImagingOpException("src and dest have different band counts."); - // Deal with bottom edge - if (edge == EDGE_ZERO_FILL) - { - float[] zeros = new float[src.getNumBands() * src.getWidth() - * (kernel.getYOrigin() - 1)]; - Arrays.fill(zeros, 0); - dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(), - kernel.getYOrigin() - 1, zeros); - } - else - { - float[] vals = new float[src.getNumBands() * src.getWidth() - * (kernel.getYOrigin() - 1)]; - src.getPixels(src.getMinX(), src.getMinY(), src.getWidth(), - kernel.getYOrigin() - 1, vals); - dest.setPixels(src.getMinX(), src.getMinY(), src.getWidth(), - kernel.getYOrigin() - 1, vals); - } + // calculate the borders that the op can't reach... + int kWidth = kernel.getWidth(); + int kHeight = kernel.getHeight(); + int left = kernel.getXOrigin(); + int right = Math.max(kWidth - left - 1, 0); + int top = kernel.getYOrigin(); + int bottom = Math.max(kHeight - top - 1, 0); - // Handle main section + // process the region that is reachable... + int regionW = src.width - left - right; + int regionH = src.height - top - bottom; float[] kvals = kernel.getKernelData(null); + float[] tmp = new float[kWidth * kHeight]; - float[] tmp = new float[kernel.getWidth() * kernel.getHeight()]; - for (int y = src.getMinY() + kernel.getYOrigin(); - y < src.getMinY() + src.getHeight() - kernel.getYOrigin() / 2; y++) - { - // Handle unfiltered edge pixels at start of line - float[] t1 = new float[(kernel.getXOrigin() - 1) * src.getNumBands()]; - if (edge == EDGE_ZERO_FILL) - Arrays.fill(t1, 0); - else - src.getPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1); - dest.setPixels(src.getMinX(), y, kernel.getXOrigin() - 1, 1, t1); - - for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + for (int x = 0; x < regionW; x++) { - // FIXME: This needs a much more efficient implementation - for (int b = 0; b < src.getNumBands(); b++) - { - float v = 0; - src.getSamples(x, y, kernel.getWidth(), kernel.getHeight(), b, tmp); - for (int i=0; i < tmp.length; i++) - v += tmp[i] * kvals[i]; - dest.setSample(x, y, b, v); - } + for (int y = 0; y < regionH; y++) + { + // FIXME: This needs a much more efficient implementation + for (int b = 0; b < src.getNumBands(); b++) + { + float v = 0; + src.getSamples(x, y, kWidth, kHeight, b, tmp); + for (int i = 0; i < tmp.length; i++) + v += tmp[tmp.length - i - 1] * kvals[i]; + // FIXME: in the above line, I've had to reverse the order of + // the samples array to make the tests pass. I haven't worked + // out why this is necessary. + dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), + b, v); + } + } } - - // Handle unfiltered edge pixels at end of line - float[] t2 = new float[(kernel.getWidth() / 2) * src.getNumBands()]; - if (edge == EDGE_ZERO_FILL) - Arrays.fill(t2, 0); - else - src.getPixels(src.getMinX() + src.getWidth() - - (kernel.getWidth() / 2), - y, kernel.getWidth() / 2, 1, t2); - dest.setPixels(src.getMinX() + src.getWidth() - (kernel.getWidth() / 2), - y, kernel.getWidth() / 2, 1, t2); - } - for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) - for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++) + + // fill in the top border + fillEdge(src, dest, 0, 0, src.width, top, edge); + + // fill in the bottom border + fillEdge(src, dest, 0, src.height - bottom, src.width, bottom, edge); + + // fill in the left border + fillEdge(src, dest, 0, top, left, regionH, edge); + + // fill in the right border + fillEdge(src, dest, src.width - right, top, right, regionH, edge); + + return dest; + } + + /** + * Fills a range of pixels (typically at the edge of a raster) with either + * zero values (if <code>edgeOp</code> is <code>EDGE_ZERO_FILL</code>) or the + * corresponding pixel values from the source raster (if <code>edgeOp</code> + * is <code>EDGE_NO_OP</code>). This utility method is called by the + * {@link #fillEdge(Raster, WritableRaster, int, int, int, int, int)} method. + * + * @param src the source raster. + * @param dest the destination raster. + * @param x the x-coordinate of the top left pixel in the range. + * @param y the y-coordinate of the top left pixel in the range. + * @param w the width of the pixel range. + * @param h the height of the pixel range. + * @param edgeOp indicates how to determine the values for the range + * (either {@link #EDGE_ZERO_FILL} or {@link #EDGE_NO_OP}). + */ + private void fillEdge(Raster src, WritableRaster dest, int x, int y, int w, + int h, int edgeOp) + { + if (w <= 0) + return; + if (h <= 0) + return; + if (edgeOp == EDGE_ZERO_FILL) // fill region with zeroes { - + float[] zeros = new float[src.getNumBands() * w * h]; + dest.setPixels(x, y, w, h, zeros); } - for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) - for (int x = src.getMinX(); x< src.getWidth() + src.getMinX(); x++) + else // copy pixels from source { - + float[] pixels = new float[src.getNumBands() * w * h]; + src.getPixels(x, y, w, h, pixels); + dest.setPixels(x, y, w, h, pixels); } - - // Handle top edge - if (edge == EDGE_ZERO_FILL) - { - float[] zeros = new float[src.getNumBands() * src.getWidth() * - (kernel.getHeight() / 2)]; - Arrays.fill(zeros, 0); - dest.setPixels(src.getMinX(), - src.getHeight() + src.getMinY() - (kernel.getHeight() / 2), - src.getWidth(), kernel.getHeight() / 2, zeros); - } - else - { - float[] vals = new float[src.getNumBands() * src.getWidth() * - (kernel.getHeight() / 2)]; - src.getPixels(src.getMinX(), - src.getHeight() + src.getMinY() - - (kernel.getHeight() / 2), - src.getWidth(), kernel.getHeight() / 2, vals); - dest.setPixels(src.getMinX(), - src.getHeight() + src.getMinY() - - (kernel.getHeight() / 2), - src.getWidth(), kernel.getHeight() / 2, vals); - } - - return dest; } /* (non-Javadoc) diff --git a/libjava/classpath/java/awt/image/DataBuffer.java b/libjava/classpath/java/awt/image/DataBuffer.java index 9e4f714180a..5a2cfd3b0e5 100644 --- a/libjava/classpath/java/awt/image/DataBuffer.java +++ b/libjava/classpath/java/awt/image/DataBuffer.java @@ -114,8 +114,7 @@ public abstract class DataBuffer */ protected DataBuffer(int dataType, int size) { - this.dataType = dataType; - this.size = size; + this(dataType, size, 1); } /** @@ -132,9 +131,7 @@ public abstract class DataBuffer * @param numBanks the number of data banks. */ protected DataBuffer(int dataType, int size, int numBanks) { - this(dataType, size); - banks = numBanks; - offsets = new int[numBanks]; + this(dataType, size, numBanks, 0); } /** @@ -153,11 +150,14 @@ public abstract class DataBuffer * @param offset the offset to the first element for all banks. */ protected DataBuffer(int dataType, int size, int numBanks, int offset) { - this(dataType, size, numBanks); - - java.util.Arrays.fill(offsets, offset); - + banks = numBanks; + this.dataType = dataType; + this.size = size; this.offset = offset; + + offsets = new int[ numBanks ]; + for(int i = 0; i < numBanks; i++ ) + offsets[i] = offset; } /** @@ -179,10 +179,11 @@ public abstract class DataBuffer * <code>numBanks != offsets.length</code>. */ protected DataBuffer(int dataType, int size, int numBanks, int[] offsets) { - this(dataType, size); if (numBanks != offsets.length) throw new ArrayIndexOutOfBoundsException(); - + + this.dataType = dataType; + this.size = size; banks = numBanks; this.offsets = offsets; diff --git a/libjava/classpath/java/awt/image/Kernel.java b/libjava/classpath/java/awt/image/Kernel.java index f7c29c3cde9..8361c0cf97d 100644 --- a/libjava/classpath/java/awt/image/Kernel.java +++ b/libjava/classpath/java/awt/image/Kernel.java @@ -1,5 +1,5 @@ /* Kernel.java -- Java class for an image processing kernel - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -44,21 +44,32 @@ package java.awt.image; * values representing a 2-dimensional array in row-major order. * * @author Jerry Quinn (jlquinn@optonline.net) - * @version 1.0 */ public class Kernel implements Cloneable { + /** The kernel width. */ private final int width; + + /** The kernel height. */ private final int height; + + /** Internal storage for the kernel's values. */ private final float[] data; /** - * Creates a new <code>Kernel</code> instance. + * Creates a new <code>Kernel</code> instance with the specified dimensions + * and values. The first <code>width * height</code> values in the specified + * <code>data</code> array are copied to internal storage. * - * @param width The 2D width of data. - * @param height The 2D height of data. - * @param data The source data array. - * @exception IllegalArgumentException if width * height < data.length. + * @param width the kernel width. + * @param height the kernel height. + * @param data the source data array (<code>null</code> not permitted). + * + * @throws IllegalArgumentException if <code>data.length</code> is less than + * <code>width * height</code>. + * @throws IllegalArgumentException if <code>width</code> or + * <code>height</code> is less than zero. + * @throws NullPointerException if <code>data</code> is <code>null</code>. */ public Kernel(int width, int height, float[] data) throws IllegalArgumentException @@ -72,7 +83,10 @@ public class Kernel implements Cloneable } /** - * Return the X origin: (width - 1) / 2 + * Returns the x-origin for the kernel, which is calculated as + * <code>(width - 1) / 2</code>. + * + * @return The x-origin for the kernel. */ public final int getXOrigin() { @@ -80,7 +94,10 @@ public class Kernel implements Cloneable } /** - * Return the Y origin: (height - 1) / 2 + * Returns the y-origin for the kernel, which is calculated as + * <code>(height - 1) / 2</code>. + * + * @return The y-origin for the kernel. */ public final int getYOrigin() { @@ -88,6 +105,8 @@ public class Kernel implements Cloneable } /** + * Returns the kernel width (as supplied to the constructor). + * * @return The kernel width. */ public final int getWidth() @@ -96,6 +115,8 @@ public class Kernel implements Cloneable } /** + * Returns the kernel height (as supplied to the constructor). + * * @return The kernel height. */ public final int getHeight() @@ -104,20 +125,25 @@ public class Kernel implements Cloneable } /** - * Return the kernel data. + * Returns an array containing a copy of the kernel data. If the + * <code>data</code> argument is non-<code>null</code>, the kernel values + * are copied into it and then <code>data</code> is returned as the result. + * If the <code>data</code> argument is <code>null</code>, this method + * allocates a new array then populates and returns it. * - * If data is null, allocates a new array and returns it. Otherwise, the - * kernel values are copied into data. - * - * @param data Array to copy values into, or null. + * @param data an array to copy the return values into (if + * <code>null</code>, a new array is allocated). + * * @return The array with copied values. - * @exception IllegalArgumentException if data != null and too small. + * + * @throws IllegalArgumentException if <code>data.length</code> is less than + * the kernel's <code>width * height</code>. */ public final float[] getKernelData(float[] data) throws IllegalArgumentException { if (data == null) - return (float[])this.data.clone(); + return (float[]) this.data.clone(); if (data.length < this.data.length) throw new IllegalArgumentException(); @@ -127,13 +153,15 @@ public class Kernel implements Cloneable } /** + * Returns a clone of this kernel. + * * @return a clone of this Kernel. */ public Object clone() { try { - return super.clone(); + return super.clone(); } catch (CloneNotSupportedException e) { diff --git a/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java index 18a6e555205..8732e57659e 100644 --- a/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java +++ b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 Free Software Foundation +/* Copyright (C) 2004, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -56,12 +56,43 @@ public class MultiPixelPackedSampleModel extends SampleModel private int numberOfBits; private int numElems; + /** + * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified + * data type, which should be one of: + * <ul> + * <li>{@link DataBuffer#TYPE_BYTE};</li> + * <li>{@link DataBuffer#TYPE_USHORT};</li> + * <li>{@link DataBuffer#TYPE_INT};</li> + * </ul> + * + * @param dataType the data type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numberOfBits the number of bits per pixel (must be a power of 2). + */ public MultiPixelPackedSampleModel(int dataType, int w, int h, int numberOfBits) { this(dataType, w, h, numberOfBits, 0, 0); } + /** + * Creates a new <code>MultiPixelPackedSampleModel</code> with the specified + * data type, which should be one of: + * <ul> + * <li>{@link DataBuffer#TYPE_BYTE};</li> + * <li>{@link DataBuffer#TYPE_USHORT};</li> + * <li>{@link DataBuffer#TYPE_INT};</li> + * </ul> + * + * @param dataType the data type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numberOfBits the number of bits per pixel (must be a power of 2). + * @param scanlineStride the number of data elements from a pixel on one + * row to the corresponding pixel in the next row. + * @param dataBitOffset the offset to the first data bit. + */ public MultiPixelPackedSampleModel(int dataType, int w, int h, int numberOfBits, int scanlineStride, int dataBitOffset) @@ -101,7 +132,7 @@ public class MultiPixelPackedSampleModel extends SampleModel // Compute scan line large enough for w pixels. if (scanlineStride == 0) - scanlineStride = ((dataBitOffset + w * numberOfBits) / elemBits); + scanlineStride = ((dataBitOffset + w * numberOfBits) - 1) / elemBits + 1; this.scanlineStride = scanlineStride; @@ -118,6 +149,16 @@ public class MultiPixelPackedSampleModel extends SampleModel } } + /** + * Creates a new <code>MultiPixelPackedSample</code> model with the same + * data type and bits per pixel as this model, but with the specified + * dimensions. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ public SampleModel createCompatibleSampleModel(int w, int h) { /* FIXME: We can avoid recalculation of bit offsets and sample @@ -126,78 +167,163 @@ public class MultiPixelPackedSampleModel extends SampleModel return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits); } - /** * Creates a DataBuffer for holding pixel data in the format and * layout described by this SampleModel. The returned buffer will * consist of one single bank. + * + * @return A new data buffer. */ public DataBuffer createDataBuffer() { - int size; - - // FIXME: The comment refers to SinglePixelPackedSampleModel. See if the - // same can be done for MultiPixelPackedSampleModel. - // We can save (scanlineStride - width) pixels at the very end of - // the buffer. The Sun reference implementation (J2SE 1.3.1 and - // 1.4.1_01) seems to do this; tested with Mauve test code. - size = scanlineStride * height; - + int size = scanlineStride * height; + if (dataBitOffset > 0) + size += (dataBitOffset - 1) / elemBits + 1; return Buffers.createBuffer(getDataType(), size); } - + /** + * Returns the number of data elements required to transfer a pixel in the + * get/setDataElements() methods. + * + * @return <code>1</code>. + */ public int getNumDataElements() { return 1; } + /** + * Returns an array containing the size (in bits) of the samples in each + * band. The <code>MultiPixelPackedSampleModel</code> class supports only + * one band, so this method returns an array with length <code>1</code>. + * + * @return An array containing the size (in bits) of the samples in band zero. + * + * @see #getSampleSize(int) + */ public int[] getSampleSize() { - return sampleSize; + return (int[]) sampleSize.clone(); } + /** + * Returns the size of the samples in the specified band. Note that the + * <code>MultiPixelPackedSampleModel</code> supports only one band -- this + * method ignored the <code>band</code> argument, and always returns the size + * of band zero. + * + * @param band the band (this parameter is ignored). + * + * @return The size of the samples in band zero. + * + * @see #getSampleSize() + */ public int getSampleSize(int band) { return sampleSize[0]; } + /** + * Returns the index in the data buffer that stores the pixel at (x, y). + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @return The index in the data buffer that stores the pixel at (x, y). + * + * @see #getBitOffset(int) + */ public int getOffset(int x, int y) { - return scanlineStride * y + ((dataBitOffset + x*numberOfBits) / elemBits); + return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits); } + /** + * The bit offset (within an element in the data buffer) of the pixels with + * the specified x-coordinate. + * + * @param x the x-coordinate. + * + * @return The bit offset. + */ public int getBitOffset(int x) { - return (dataBitOffset + x*numberOfBits) % elemBits; + return (dataBitOffset + x * numberOfBits) % elemBits; } + /** + * Returns the offset to the first data bit. + * + * @return The offset to the first data bit. + */ public int getDataBitOffset() { return dataBitOffset; } + /** + * Returns the number of data elements from a pixel in one row to the + * corresponding pixel in the next row. + * + * @return The scanline stride. + */ public int getScanlineStride() { return scanlineStride; } + /** + * Returns the number of bits per pixel. + * + * @return The number of bits per pixel. + */ public int getPixelBitStride() { return numberOfBits; } + + /** + * Returns the transfer type, which is one of the following (depending on + * the number of bits per sample for this model): + * <ul> + * <li>{@link DataBuffer#TYPE_BYTE};</li> + * <li>{@link DataBuffer#TYPE_USHORT};</li> + * <li>{@link DataBuffer#TYPE_INT};</li> + * </ul> + * + * @return The transfer type. + */ + public int getTransferType() + { + if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE)) + return DataBuffer.TYPE_BYTE; + else if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_USHORT)) + return DataBuffer.TYPE_USHORT; + return DataBuffer.TYPE_INT; + } - + /** + * Normally this method returns a sample model for accessing a subset of + * bands of image data, but since <code>MultiPixelPackedSampleModel</code> + * only supports a single band, this overridden implementation just returns + * a new instance of <code>MultiPixelPackedSampleModel</code>, with the same + * attributes as this instance. + * + * @param bands the bands to include in the subset (this is ignored, except + * that if it is non-<code>null</code> a check is made to ensure that the + * array length is equal to <code>1</code>). + * + * @throws RasterFormatException if <code>bands</code> is not + * <code>null</code> and <code>bands.length != 1</code>. + */ public SampleModel createSubsetSampleModel(int[] bands) { - int numBands = bands.length; - if (numBands != 1) + if (bands != null && bands.length != 1) throw new RasterFormatException("MultiPixelPackedSampleModel only" - + " supports one band"); - - return new MultiPixelPackedSampleModel(dataType, width, height, - numberOfBits, scanlineStride, - dataBitOffset); + + " supports one band"); + return new MultiPixelPackedSampleModel(dataType, width, height, + numberOfBits, scanlineStride, dataBitOffset); } /** @@ -207,68 +333,82 @@ public class MultiPixelPackedSampleModel extends SampleModel * array obj, since there is only one band. If obj is null, a new array of * getTransferType() is created. * - * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. - * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. - * @param obj The primitive array to store the pixels into or null to force creation. + * @param x The x-coordinate of the pixel rectangle to store in + * <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in + * <code>obj</code>. + * @param obj The primitive array to store the pixels into or null to force + * creation. * @param data The DataBuffer that is the source of the pixel data. * @return The primitive array containing the pixel data. - * @see java.awt.image.SampleModel#getDataElements(int, int, java.lang.Object, java.awt.image.DataBuffer) + * @see java.awt.image.SampleModel#getDataElements(int, int, Object, + * DataBuffer) */ - public Object getDataElements(int x, int y, Object obj, - DataBuffer data) + public Object getDataElements(int x, int y, Object obj, DataBuffer data) { int pixel = getSample(x, y, 0, data); switch (getTransferType()) - { - case DataBuffer.TYPE_BYTE: - if (obj == null) obj = new byte[1]; - ((byte[])obj)[0] = (byte)pixel; - return obj; - case DataBuffer.TYPE_USHORT: - if (obj == null) obj = new short[1]; - ((short[])obj)[0] = (short)pixel; - return obj; - case DataBuffer.TYPE_INT: - if (obj == null) obj = new int[1]; - ((int[])obj)[0] = pixel; - return obj; - default: - // Seems like the only sensible thing to do. - throw new ClassCastException(); - } + { + case DataBuffer.TYPE_BYTE: + if (obj == null) + obj = new byte[1]; + ((byte[]) obj)[0] = (byte) pixel; + return obj; + case DataBuffer.TYPE_USHORT: + if (obj == null) + obj = new short[1]; + ((short[]) obj)[0] = (short) pixel; + return obj; + case DataBuffer.TYPE_INT: + if (obj == null) + obj = new int[1]; + ((int[]) obj)[0] = pixel; + return obj; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } } + /** + * Returns an array (of length 1) containing the sample for the pixel at + * (x, y) in the specified data buffer. If <code>iArray</code> is not + * <code>null</code>, it will be populated with the sample value and + * returned as the result of this function (this avoids allocating a new + * array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return An array containing the pixel sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { - if (iArray == null) iArray = new int[1]; + if (iArray == null) + iArray = new int[1]; iArray[0] = getSample(x, y, 0, data); - return iArray; } - public int[] getPixels(int x, int y, int w, int h, int[] iArray, - DataBuffer data) - { - int offset = getOffset(x, y); - if (iArray == null) iArray = new int[w*h]; - int outOffset = 0; - for (y=0; y<h; y++) - { - int lineOffset = offset; - for (x=0; x<w;) - { - int samples = data.getElem(lineOffset++); - for (int b=0; b<numElems && x<w; b++) - { - iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; - x++; - } - } - offset += scanlineStride; - } - return iArray; - } - + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int getSample(int x, int y, int b, DataBuffer data) { int pos = @@ -286,72 +426,82 @@ public class MultiPixelPackedSampleModel extends SampleModel * @param y The y-coordinate of the data elements in <code>obj</code>. * @param obj The primitive array containing the data elements to set. * @param data The DataBuffer to store the data elements into. - * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { int transferType = getTransferType(); - if (getTransferType() != data.getDataType()) - { - throw new IllegalArgumentException("transfer type ("+ - getTransferType()+"), "+ - "does not match data "+ - "buffer type (" + - data.getDataType() + - ")."); - } - - int offset = getOffset(x, y); - try { - switch (transferType) - { - case DataBuffer.TYPE_BYTE: - { - DataBufferByte out = (DataBufferByte) data; - byte[] in = (byte[]) obj; - out.getData()[offset] = in[0]; - return; - } - case DataBuffer.TYPE_USHORT: - { - DataBufferUShort out = (DataBufferUShort) data; - short[] in = (short[]) obj; - out.getData()[offset] = in[0]; - return; - } - case DataBuffer.TYPE_INT: - { - DataBufferInt out = (DataBufferInt) data; - int[] in = (int[]) obj; - out.getData()[offset] = in[0]; - return; - } - default: - throw new ClassCastException("Unsupported data type"); - } + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + byte[] in = (byte[]) obj; + setSample(x, y, 0, in[0] & 0xFF, data); + return; + } + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; + setSample(x, y, 0, in[0] & 0xFFFF, data); + return; + } + case DataBuffer.TYPE_INT: + { + int[] in = (int[]) obj; + setSample(x, y, 0, in[0], data); + return; + } + default: + throw new ClassCastException("Unsupported data type"); + } } catch (ArrayIndexOutOfBoundsException aioobe) { - String msg = "While writing data elements" + - ", x="+x+", y="+y+ - ", width="+width+", height="+height+ - ", scanlineStride="+scanlineStride+ - ", offset="+offset+ - ", data.getSize()="+data.getSize()+ - ", data.getOffset()="+data.getOffset()+ - ": " + - aioobe; - throw new ArrayIndexOutOfBoundsException(msg); + String msg = "While writing data elements" + + ", x=" + x + ", y=" + y + + ", width=" + width + ", height=" + height + + ", scanlineStride=" + scanlineStride + + ", offset=" + getOffset(x, y) + + ", data.getSize()=" + data.getSize() + + ", data.getOffset()=" + data.getOffset() + + ": " + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); } - } + } + /** + * Sets the sample value for the pixel at (x, y) in the specified data + * buffer to the specified value. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample value (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + * + * @see #setSample(int, int, int, int, DataBuffer) + */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { setSample(x, y, 0, iArray[0], data); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, int s, DataBuffer data) { int bitpos = @@ -367,6 +517,70 @@ public class MultiPixelPackedSampleModel extends SampleModel } /** + * Tests this sample model for equality with an arbitrary object. This + * method returns <code>true</code> if and only if: + * <ul> + * <li><code>obj</code> is not <code>null</code>; + * <li><code>obj</code> is an instance of + * <code>MultiPixelPackedSampleModel</code>; + * <li>both models have the same: + * <ul> + * <li><code>dataType</code>; + * <li><code>width</code>; + * <li><code>height</code>; + * <li><code>numberOfBits</code>; + * <li><code>scanlineStride</code>; + * <li><code>dataBitOffsets</code>. + * </ul> + * </li> + * </ul> + * + * @param obj the object (<code>null</code> permitted) + * + * @return <code>true</code> if this model is equal to <code>obj</code>, and + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof MultiPixelPackedSampleModel)) + return false; + MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj; + if (this.dataType != that.dataType) + return false; + if (this.width != that.width) + return false; + if (this.height != that.height) + return false; + if (this.numberOfBits != that.numberOfBits) + return false; + if (this.scanlineStride != that.scanlineStride) + return false; + if (this.dataBitOffset != that.dataBitOffset) + return false; + return true; + } + + /** + * Returns a hash code for this <code>MultiPixelPackedSampleModel</code>. + * + * @return A hash code. + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + result = 37 * result + dataType; + result = 37 * result + width; + result = 37 * result + height; + result = 37 * result + numberOfBits; + result = 37 * result + scanlineStride; + result = 37 * result + dataBitOffset; + return result; + } + + /** * Creates a String with some information about this SampleModel. * @return A String describing this SampleModel. * @see java.lang.Object#toString() diff --git a/libjava/classpath/java/awt/image/Raster.java b/libjava/classpath/java/awt/image/Raster.java index 4af958a17c7..160f8be8b51 100644 --- a/libjava/classpath/java/awt/image/Raster.java +++ b/libjava/classpath/java/awt/image/Raster.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2000, 2002, 2003 Free Software Foundation +/* Copyright (C) 2000, 2002, 2003, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -41,39 +41,80 @@ import java.awt.Point; import java.awt.Rectangle; /** + * A rectangular collection of pixels composed from a {@link DataBuffer} which + * stores the pixel values, and a {@link SampleModel} which is used to retrieve + * the pixel values. + * * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) */ public class Raster { + /** The sample model used to access the pixel values. */ protected SampleModel sampleModel; + + /** The data buffer used to store the pixel values. */ protected DataBuffer dataBuffer; + + /** The x-coordinate of the top left corner of the raster. */ protected int minX; + + /** The y-coordinate of the top left corner of the raster. */ protected int minY; + + /** The width of the raster. */ protected int width; + + /** The height of the raster. */ protected int height; + protected int sampleModelTranslateX; + protected int sampleModelTranslateY; + + /** The number of bands. */ protected int numBands; + protected int numDataElements; + + /** The raster's parent. */ protected Raster parent; + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param origin the origin. + */ protected Raster(SampleModel sampleModel, Point origin) { this(sampleModel, sampleModel.createDataBuffer(), origin); } + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param origin the origin. + */ protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, - Point origin) + Point origin) { - this(sampleModel, dataBuffer, - new Rectangle(origin.x, origin.y, - sampleModel.getWidth(), sampleModel.getHeight()), - origin, null); + this(sampleModel, dataBuffer, new Rectangle(origin.x, origin.y, + sampleModel.getWidth(), sampleModel.getHeight()), origin, null); } + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param aRegion the raster's bounds. + * @param sampleModelTranslate the translation (<code>null</code> permitted). + * @param parent the raster's parent. + */ protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, - Rectangle aRegion, - Point sampleModelTranslate, Raster parent) + Rectangle aRegion, Point sampleModelTranslate, Raster parent) { this.sampleModel = sampleModel; this.dataBuffer = dataBuffer; @@ -95,70 +136,127 @@ public class Raster this.parent = parent; } + /** + * Creates an interleaved raster using the specified data type. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param location + * + * @return The new raster. + */ public static WritableRaster createInterleavedRaster(int dataType, - int w, int h, - int bands, - Point location) + int w, int h, int bands, Point location) { int[] bandOffsets = new int[bands]; // TODO: Maybe not generate this every time. - for (int b=0; b<bands; b++) bandOffsets[b] = b; + for (int b = 0; b < bands; b++) + bandOffsets[b] = b; - int scanlineStride = bands*w; + int scanlineStride = bands * w; return createInterleavedRaster(dataType, w, h, scanlineStride, bands, - bandOffsets, location); + bandOffsets, location); } - public static WritableRaster createInterleavedRaster(int dataType, - int w, int h, - int scanlineStride, - int pixelStride, - int[] bandOffsets, - Point location) - { - SampleModel sm = new ComponentSampleModel(dataType, - w, h, - pixelStride, - scanlineStride, - bandOffsets); + /** + * Creates an interleaved raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param pixelStride the number of elements from a sample in one pixel to + * the corresponding sample in the next pixel. + * @param bandOffsets the band offsets. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, int scanlineStride, int pixelStride, int[] bandOffsets, + Point location) + { + SampleModel sm = new ComponentSampleModel(dataType, w, h, pixelStride, + scanlineStride, bandOffsets); return createWritableRaster(sm, location); } - public static WritableRaster createBandedRaster(int dataType, - int w, int h, int bands, - Point location) + /** + * Creates a new banded raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, + int bands, Point location) { SampleModel sm = new BandedSampleModel(dataType, w, h, bands); return createWritableRaster(sm, location); } - public static WritableRaster createBandedRaster(int dataType, - int w, int h, - int scanlineStride, - int[] bankIndices, - int[] bandOffsets, - Point location) + /** + * Creates a new banded raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bankIndices the index for each bank. + * @param bandOffsets the offset for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, + int scanlineStride, int[] bankIndices, int[] bandOffsets, Point location) { SampleModel sm = new BandedSampleModel(dataType, w, h, scanlineStride, - bankIndices, bandOffsets); + bankIndices, bandOffsets); return createWritableRaster(sm, location); } - public static WritableRaster createPackedRaster(int dataType, - int w, int h, - int[] bandMasks, - Point location) + /** + * Creates a new packed raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bandMasks the bit mask for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(int dataType, int w, int h, + int[] bandMasks, Point location) { - SampleModel sm = new SinglePixelPackedSampleModel(dataType, - w, h, - bandMasks); + SampleModel sm = new SinglePixelPackedSampleModel(dataType, w, h, + bandMasks); return createWritableRaster(sm, location); } + /** + * Creates a new raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param bitsPerBand the number of bits per band. + * @param location + * + * @return The new raster. + */ public static WritableRaster createPackedRaster(int dataType, - int w, int h, - int bands, int bitsPerBand, - Point location) + int w, int h, int bands, int bitsPerBand, Point location) { if (bands <= 0 || (bands * bitsPerBand > getTypeBits(dataType))) throw new IllegalArgumentException(); @@ -166,135 +264,238 @@ public class Raster SampleModel sm; if (bands == 1) - sm = new MultiPixelPackedSampleModel(dataType, w, h, bitsPerBand); + sm = new MultiPixelPackedSampleModel(dataType, w, h, bitsPerBand); else { - int[] bandMasks = new int[bands]; - int mask = 0x1; - for (int bits = bitsPerBand; --bits != 0;) - mask = (mask << 1) | 0x1; - for (int i = 0; i < bands; i++) - { - bandMasks[i] = mask; - mask <<= bitsPerBand; - } - - sm = new SinglePixelPackedSampleModel(dataType, w, h, bandMasks); + int[] bandMasks = new int[bands]; + int mask = 0x1; + for (int bits = bitsPerBand; --bits != 0;) + mask = (mask << 1) | 0x1; + for (int i = 0; i < bands; i++) + { + bandMasks[i] = mask; + mask <<= bitsPerBand; + } + + sm = new SinglePixelPackedSampleModel(dataType, w, h, bandMasks); } return createWritableRaster(sm, location); } - public static WritableRaster - createInterleavedRaster(DataBuffer dataBuffer, int w, int h, - int scanlineStride, int pixelStride, - int[] bandOffsets, Point location) + /** + * Creates a new interleaved raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param pixelStride the number of elements from a sample in one pixel to + * the corresponding sample in the next pixel. + * @param bandOffsets the offset for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int pixelStride, int[] bandOffsets, + Point location) { SampleModel sm = new ComponentSampleModel(dataBuffer.getDataType(), - w, h, - scanlineStride, - pixelStride, - bandOffsets); + w, h, scanlineStride, pixelStride, bandOffsets); return createWritableRaster(sm, dataBuffer, location); } - public static - WritableRaster createBandedRaster(DataBuffer dataBuffer, - int w, int h, - int scanlineStride, - int[] bankIndices, - int[] bandOffsets, - Point location) + /** + * Creates a new banded raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bankIndices the index for each bank. + * @param bandOffsets the band offsets. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int[] bankIndices, int[] bandOffsets, + Point location) { SampleModel sm = new BandedSampleModel(dataBuffer.getDataType(), - w, h, scanlineStride, - bankIndices, bandOffsets); + w, h, scanlineStride, bankIndices, bandOffsets); return createWritableRaster(sm, dataBuffer, location); } - public static WritableRaster - createPackedRaster(DataBuffer dataBuffer, - int w, int h, - int scanlineStride, - int[] bandMasks, - Point location) + /** + * Creates a new packed raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bandMasks the bit mask for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int[] bandMasks, Point location) { - SampleModel sm = - new SinglePixelPackedSampleModel(dataBuffer.getDataType(), - w, h, - scanlineStride, - bandMasks); + SampleModel sm = new SinglePixelPackedSampleModel(dataBuffer.getDataType(), + w, h, scanlineStride, bandMasks); return createWritableRaster(sm, dataBuffer, location); } - public static WritableRaster - createPackedRaster(DataBuffer dataBuffer, - int w, int h, - int bitsPerPixel, - Point location) - { - SampleModel sm = - new MultiPixelPackedSampleModel(dataBuffer.getDataType(), - w, h, - bitsPerPixel); + /** + * Creates a new packed raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param bitsPerPixel the number of bits per pixel. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, int bitsPerPixel, Point location) + { + SampleModel sm = new MultiPixelPackedSampleModel(dataBuffer.getDataType(), + w, h, bitsPerPixel); return createWritableRaster(sm, dataBuffer, location); } + /** + * Creates a new raster. + * + * @param sm the sample model. + * @param db the data buffer. + * @param location + * + * @return The new raster. + */ public static Raster createRaster(SampleModel sm, DataBuffer db, - Point location) + Point location) { return new Raster(sm, db, location); } + /** + * Creates a new writable raster. + * + * @param sm the sample model. + * @param location + * + * @return The new writable raster. + */ public static WritableRaster createWritableRaster(SampleModel sm, - Point location) + Point location) { return new WritableRaster(sm, location); } + /** + * Creates a new writable raster. + * + * @param sm the sample model. + * @param db the data buffer. + * @param location + * + * @return The new writable raster. + */ public static WritableRaster createWritableRaster(SampleModel sm, - DataBuffer db, - Point location) + DataBuffer db, Point location) { return new WritableRaster(sm, db, location); } + /** + * Returns the raster's parent. + * + * @return The raster's parent. + */ public Raster getParent() { return parent; } + /** + * Returns the x-translation. + * + * @return The x-translation. + */ public final int getSampleModelTranslateX() { return sampleModelTranslateX; } + /** + * Returns the y-translation. + * + * @return The y-translation. + */ public final int getSampleModelTranslateY() { return sampleModelTranslateY; } + /** + * Creates a new writable raster that is compatible with this raster. + * + * @return A new writable raster. + */ public WritableRaster createCompatibleWritableRaster() { return new WritableRaster(getSampleModel(), new Point(minX, minY)); } + /** + * Creates a new writable raster that is compatible with this raster. + * + * @param w the width. + * @param h the height. + * + * @return A new writable raster. + */ public WritableRaster createCompatibleWritableRaster(int w, int h) { return createCompatibleWritableRaster(minX, minY, w, h); } + /** + * Creates a new writable raster that is compatible with this raster, with + * the specified bounds. + * + * @param rect the raster bounds. + * + * @return A new writable raster. + */ public WritableRaster createCompatibleWritableRaster(Rectangle rect) { return createCompatibleWritableRaster(rect.x, rect.y, - rect.width, rect.height); + rect.width, rect.height); } + /** + * Creates a new writable raster that is compatible with this raster, with + * the specified bounds. + * + * @param x the x-coordinate of the top-left corner of the raster. + * @param y the y-coordinate of the top-left corner of the raster. + * @param w the raster width. + * @param h the raster height. + * + * @return A new writable raster. + */ public WritableRaster createCompatibleWritableRaster(int x, int y, - int w, int h) + int w, int h) { SampleModel sm = getSampleModel().createCompatibleSampleModel(w, h); - return new WritableRaster(sm, sm.createDataBuffer(), - new Point(x, y)); + return new WritableRaster(sm, sm.createDataBuffer(), new Point(x, y)); } public Raster createTranslatedChild(int childMinX, int childMinY) { @@ -302,15 +503,13 @@ public class Raster int tcy = sampleModelTranslateY - minY + childMinY; return new Raster(sampleModel, dataBuffer, - new Rectangle(childMinX, childMinY, - width, height), - new Point(tcx, tcy), - this); + new Rectangle(childMinX, childMinY, width, height), + new Point(tcx, tcy), this); } public Raster createChild(int parentX, int parentY, int width, - int height, int childMinX, int childMinY, - int[] bandList) + int height, int childMinX, int childMinY, + int[] bandList) { /* FIXME: Throw RasterFormatException if child bounds extends beyond the bounds of this raster. */ @@ -343,38 +542,67 @@ public class Raster */ return new Raster(sm, dataBuffer, - new Rectangle(childMinX, childMinY, - width, height), - new Point(sampleModelTranslateX+childMinX-parentX, - sampleModelTranslateY+childMinY-parentY), - this); + new Rectangle(childMinX, childMinY, width, height), + new Point(sampleModelTranslateX + childMinX - parentX, + sampleModelTranslateY + childMinY - parentY), + this); } + /** + * Returns a new rectangle containing the bounds of this raster. + * + * @return A new rectangle containing the bounds of this raster. + */ public Rectangle getBounds() { return new Rectangle(minX, minY, width, height); } + /** + * Returns the x-coordinate of the top left corner of the raster. + * + * @return The x-coordinate of the top left corner of the raster. + */ public final int getMinX() { return minX; } + /** + * Returns the t-coordinate of the top left corner of the raster. + * + * @return The t-coordinate of the top left corner of the raster. + */ public final int getMinY() { return minY; } + /** + * Returns the width of the raster. + * + * @return The width of the raster. + */ public final int getWidth() { return width; } + /** + * Returns the height of the raster. + * + * @return The height of the raster. + */ public final int getHeight() { return height; } + /** + * Returns the number of bands for this raster. + * + * @return The number of bands. + */ public final int getNumBands() { return numBands; @@ -384,17 +612,34 @@ public class Raster { return numDataElements; } - + + /** + * Returns the transfer type for the raster (this is determined by the + * raster's sample model). + * + * @return The transfer type. + */ public final int getTransferType() { return sampleModel.getTransferType(); } + /** + * Returns the data buffer that stores the pixel data for this raster. + * + * @return The data buffer. + */ public DataBuffer getDataBuffer() { return dataBuffer; } + /** + * Returns the sample model that accesses the data buffer (to extract pixel + * data) for this raster. + * + * @return The sample model. + */ public SampleModel getSampleModel() { return sampleModel; @@ -402,112 +647,275 @@ public class Raster public Object getDataElements(int x, int y, Object outData) { - return sampleModel.getDataElements(x-sampleModelTranslateX, - y-sampleModelTranslateY, - outData, dataBuffer); + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, outData, dataBuffer); } - public Object getDataElements(int x, int y, int w, int h, - Object outData) + public Object getDataElements(int x, int y, int w, int h, Object outData) { - return sampleModel.getDataElements(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, outData, dataBuffer); + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, outData, dataBuffer); } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If <code>iArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ public int[] getPixel(int x, int y, int[] iArray) { - return sampleModel.getPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - iArray, dataBuffer); + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, iArray, dataBuffer); } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If <code>fArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ public float[] getPixel(int x, int y, float[] fArray) { - return sampleModel.getPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - fArray, dataBuffer); + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, fArray, dataBuffer); } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If <code>dArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ public double[] getPixel(int x, int y, double[] dArray) { - return sampleModel.getPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - dArray, dataBuffer); + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, dArray, dataBuffer); } + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If <code>iArray</code> is not <code>null</code>, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ public int[] getPixels(int x, int y, int w, int h, int[] iArray) { - return sampleModel.getPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, iArray, dataBuffer); + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, iArray, dataBuffer); } - public float[] getPixels(int x, int y, int w, int h, - float[] fArray) + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If <code>fArray</code> is not <code>null</code>, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ + public float[] getPixels(int x, int y, int w, int h, float[] fArray) { - return sampleModel.getPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, fArray, dataBuffer); + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, fArray, dataBuffer); } - public double[] getPixels(int x, int y, int w, int h, - double[] dArray) + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If <code>dArray</code> is not <code>null</code>, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The pixel sample values. + */ + public double[] getPixels(int x, int y, int w, int h, double[] dArray) { - return sampleModel.getPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, dArray, dataBuffer); + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, dArray, dataBuffer); } + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * + * @return The sample value. + */ public int getSample(int x, int y, int b) { - return sampleModel.getSample(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, dataBuffer); + return sampleModel.getSample(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); } + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * + * @return The sample value. + * + * @see #getSample(int, int, int) + */ public float getSampleFloat(int x, int y, int b) { - return sampleModel.getSampleFloat(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, dataBuffer); + return sampleModel.getSampleFloat(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); } + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * + * @return The sample value. + * + * @see #getSample(int, int, int) + */ public double getSampleDouble(int x, int y, int b) { - return sampleModel.getSampleDouble(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, dataBuffer); + return sampleModel.getSampleDouble(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); } + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * <code>iArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The sample values. + */ public int[] getSamples(int x, int y, int w, int h, int b, - int[] iArray) + int[] iArray) { - return sampleModel.getSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, iArray, dataBuffer); + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, iArray, dataBuffer); } - public float[] getSamples(int x, int y, int w, int h, int b, - float[] fArray) - { - return sampleModel.getSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, fArray, dataBuffer); + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * <code>fArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The sample values. + */ + public float[] getSamples(int x, int y, int w, int h, int b, float[] fArray) + { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, fArray, dataBuffer); } - public double[] getSamples(int x, int y, int w, int h, int b, - double[] dArray) + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * <code>dArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * + * @return The sample values. + */ + public double[] getSamples(int x, int y, int w, int h, int b, + double[] dArray) { - return sampleModel.getSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, dArray, dataBuffer); + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, dArray, dataBuffer); } /** - * Create a String representing the stat of this Raster. + * Create a String representing the state of this Raster. + * * @return A String representing the stat of this Raster. - * @see java.lang.Object#toString() */ public String toString() { @@ -524,23 +932,39 @@ public class Raster return result.toString(); } - // Map from datatype to bits + /** + * Returns the number of bits used to represent the specified data type. + * Valid types are: + * <ul> + * <li>{@link DataBuffer#TYPE_BYTE};</li> + * <li>{@link DataBuffer#TYPE_USHORT};</li> + * <li>{@link DataBuffer#TYPE_SHORT};</li> + * <li>{@link DataBuffer#TYPE_INT};</li> + * <li>{@link DataBuffer#TYPE_FLOAT};</li> + * <li>{@link DataBuffer#TYPE_DOUBLE};</li> + * </ul> + * This method returns 0 for invalid data types. + * + * @param dataType the data type. + * + * @return The number of bits used to represent the specified data type. + */ private static int getTypeBits(int dataType) { switch (dataType) { case DataBuffer.TYPE_BYTE: - return 8; + return 8; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: - return 16; + return 16; case DataBuffer.TYPE_INT: case DataBuffer.TYPE_FLOAT: - return 32; + return 32; case DataBuffer.TYPE_DOUBLE: - return 64; + return 64; default: - return 0; + return 0; } } } diff --git a/libjava/classpath/java/awt/image/RasterOp.java b/libjava/classpath/java/awt/image/RasterOp.java index e081ca3d2ad..656370e8bcc 100644 --- a/libjava/classpath/java/awt/image/RasterOp.java +++ b/libjava/classpath/java/awt/image/RasterOp.java @@ -1,5 +1,5 @@ /* RasterOp.java -- - Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation + Copyright (C) 2000, 2002, 2004, 2005, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -42,16 +42,64 @@ import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +/** + * An operation that is performed on one raster (the source) producing a new + * raster (the destination). + */ public interface RasterOp { + /** + * Performs an operation on the source raster, returning the result in a + * writable raster. If <code>dest</code> is <code>null</code>, a new + * <code>WritableRaster</code> will be created by calling the + * {@link #createCompatibleDestRaster(Raster)} method. If <code>dest</code> + * is not <code>null</code>, the result is written to <code>dest</code> then + * returned (this avoids creating a new <code>WritableRaster</code> each + * time this method is called). + * + * @param src the source raster. + * @param dest the destination raster (<code>null</code> permitted). + * + * @return The filtered raster. + */ WritableRaster filter(Raster src, WritableRaster dest); + /** + * Returns the bounds of the destination raster on the basis of this + * <code>RasterOp</code> being applied to the specified source raster. + * + * @param src the source raster. + * + * @return The destination bounds. + */ Rectangle2D getBounds2D(Raster src); + /** + * Returns a raster that can be used by this <code>RasterOp</code> as the + * destination raster when operating on the specified source raster. + * + * @param src the source raster. + * + * @return A new writable raster that can be used as the destination raster. + */ WritableRaster createCompatibleDestRaster(Raster src); + /** + * Returns the point on the destination raster that corresponds to the given + * point on the source raster. + * + * @param srcPoint the source point. + * @param destPoint the destination point (<code>null</code> permitted). + * + * @return The destination point. + */ Point2D getPoint2D(Point2D srcPoint, Point2D destPoint); + /** + * Returns the rendering hints for this operation. + * + * @return The rendering hints. + */ RenderingHints getRenderingHints(); } diff --git a/libjava/classpath/java/awt/image/SampleModel.java b/libjava/classpath/java/awt/image/SampleModel.java index 6e3fd4069a3..cb352bb4d85 100644 --- a/libjava/classpath/java/awt/image/SampleModel.java +++ b/libjava/classpath/java/awt/image/SampleModel.java @@ -37,6 +37,9 @@ exception statement from your version. */ package java.awt.image; /** + * A <code>SampleModel</code> is used to access pixel data from a + * {@link DataBuffer}. This is used by the {@link Raster} class. + * * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) */ public abstract class SampleModel @@ -100,16 +103,37 @@ public abstract class SampleModel this.numBands = numBands; } + /** + * Returns the width of the pixel data accessible via this + * <code>SampleModel</code>. + * + * @return The width. + * + * @see #getHeight() + */ public final int getWidth() { return width; } + /** + * Returns the height of the pixel data accessible via this + * <code>SampleModel</code>. + * + * @return The height. + * + * @see #getWidth() + */ public final int getHeight() { return height; } + /** + * Returns the number of bands for this <code>SampleModel</code>. + * + * @return The number of bands. + */ public final int getNumBands() { return numBands; @@ -117,6 +141,12 @@ public abstract class SampleModel public abstract int getNumDataElements(); + /** + * Returns the type of the {@link DataBuffer} that this + * <code>SampleModel</code> accesses. + * + * @return The data buffer type. + */ public final int getDataType() { return dataType; @@ -128,6 +158,22 @@ public abstract class SampleModel return dataType; } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If <code>iArray</code> is not <code>null</code>, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { if (iArray == null) @@ -234,6 +280,22 @@ public abstract class SampleModel } } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If <code>fArray</code> is not <code>null</code>, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public float[] getPixel(int x, int y, float[] fArray, DataBuffer data) { if (fArray == null) @@ -246,6 +308,22 @@ public abstract class SampleModel return fArray; } + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If <code>dArray</code> is not <code>null</code>, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public double[] getPixel(int x, int y, double[] dArray, DataBuffer data) { if (dArray == null) dArray = new double[numBands]; @@ -256,8 +334,27 @@ public abstract class SampleModel return dArray; } - /* FIXME: Should it return a banded or pixel interleaved array of - samples? (Assume interleaved.) */ + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If <code>iArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { @@ -278,8 +375,27 @@ public abstract class SampleModel return iArray; } - /* FIXME: Should it return a banded or pixel interleaved array of - samples? (Assume interleaved.) */ + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If <code>fArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public float[] getPixels(int x, int y, int w, int h, float[] fArray, DataBuffer data) { @@ -299,8 +415,27 @@ public abstract class SampleModel return fArray; } - /* FIXME: Should it return a banded or pixel interleaved array of - samples? (Assume interleaved.) */ + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If <code>dArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public double[] getPixels(int x, int y, int w, int h, double[] dArray, DataBuffer data) { @@ -321,18 +456,85 @@ public abstract class SampleModel return dArray; } + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public abstract int getSample(int x, int y, int b, DataBuffer data); + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + * + * @see #getSample(int, int, int, DataBuffer) + */ public float getSampleFloat(int x, int y, int b, DataBuffer data) { return getSample(x, y, b, data); } + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + * + * @see #getSample(int, int, int, DataBuffer) + */ public double getSampleDouble(int x, int y, int b, DataBuffer data) { return getSampleFloat(x, y, b, data); } + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * <code>iArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { @@ -350,6 +552,27 @@ public abstract class SampleModel return iArray; } + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * <code>fArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param fArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public float[] getSamples(int x, int y, int w, int h, int b, float[] fArray, DataBuffer data) { @@ -367,6 +590,27 @@ public abstract class SampleModel return fArray; } + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * <code>dArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param dArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public double[] getSamples(int x, int y, int w, int h, int b, double[] dArray, DataBuffer data) { @@ -384,24 +628,77 @@ public abstract class SampleModel return dArray; } + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { for (int b = 0; b < numBands; b++) setSample(x, y, b, iArray[b], data); } + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>fArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixel(int x, int y, float[] fArray, DataBuffer data) { for (int b = 0; b < numBands; b++) setSample(x, y, b, fArray[b], data); } + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>dArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixel(int x, int y, double[] dArray, DataBuffer data) { for (int b = 0; b < numBands; b++) setSample(x, y, b, dArray[b], data); } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { @@ -418,6 +715,23 @@ public abstract class SampleModel } } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray the pixel sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>fArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, float[] fArray, DataBuffer data) { @@ -434,6 +748,23 @@ public abstract class SampleModel } } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray the pixel sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>dArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, double[] dArray, DataBuffer data) { @@ -450,21 +781,76 @@ public abstract class SampleModel } } + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public abstract void setSample(int x, int y, int b, int s, DataBuffer data); + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, float s, DataBuffer data) { setSample(x, y, b, (int) s, data); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, double s, DataBuffer data) { setSample(x, y, b, (float) s, data); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param iArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { @@ -475,6 +861,22 @@ public abstract class SampleModel setSample(xx, yy, b, iArray[inOffset++], data); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param fArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, float[] fArray, DataBuffer data) { @@ -486,6 +888,22 @@ public abstract class SampleModel } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param dArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, double[] dArray, DataBuffer data) { int size = w * h; @@ -495,6 +913,15 @@ public abstract class SampleModel setSample(xx, yy, b, dArray[inOffset++], data); } + /** + * Creates a new <code>SampleModel</code> that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ public abstract SampleModel createCompatibleSampleModel(int w, int h); /** @@ -510,9 +937,31 @@ public abstract class SampleModel */ public abstract SampleModel createSubsetSampleModel(int[] bands); + /** + * Creates a new {@link DataBuffer} of the correct type and size for this + * <code>SampleModel</code>. + * + * @return The data buffer. + */ public abstract DataBuffer createDataBuffer(); + /** + * Returns an array containing the size (in bits) for each band accessed by + * the <code>SampleModel</code>. + * + * @return An array. + * + * @see #getSampleSize(int) + */ public abstract int[] getSampleSize(); + /** + * Returns the size (in bits) of the samples for the specified band. + * + * @param band the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * + * @return The sample size (in bits). + */ public abstract int getSampleSize(int band); } diff --git a/libjava/classpath/java/awt/image/ShortLookupTable.java b/libjava/classpath/java/awt/image/ShortLookupTable.java index 5915a7939a3..858818cf26d 100644 --- a/libjava/classpath/java/awt/image/ShortLookupTable.java +++ b/libjava/classpath/java/awt/image/ShortLookupTable.java @@ -1,5 +1,5 @@ /* ShortLookupTable.java -- Java class for a pixel translation table. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -67,7 +67,13 @@ public class ShortLookupTable extends LookupTable throws IllegalArgumentException { super(offset, data.length); - this.data = data; + + // tests show that Sun's implementation creates a new array to store the + // references from the incoming 'data' array - not sure why, but we'll + // match that behaviour just in case it matters... + this.data = new short[data.length][]; + for (int i = 0; i < data.length; i++) + this.data[i] = data[i]; } /** @@ -77,17 +83,25 @@ public class ShortLookupTable extends LookupTable * table. The same table is applied to all pixel components. * * @param offset Offset to be subtracted. - * @param data Lookup table for all components. + * @param data Lookup table for all components (<code>null</code> not + * permitted). * @exception IllegalArgumentException if offset < 0. */ public ShortLookupTable(int offset, short[] data) throws IllegalArgumentException { super(offset, 1); + if (data == null) + throw new NullPointerException("Null 'data' argument."); this.data = new short[][] {data}; } - /** Return the lookup tables. */ + /** + * Return the lookup tables. This is a reference to the actual table, so + * modifying the contents of the returned array will modify the lookup table. + * + * @return The lookup table. + */ public final short[][] getTable() { return data; @@ -117,11 +131,11 @@ public class ShortLookupTable extends LookupTable dst = new int[src.length]; if (data.length == 1) - for (int i=0; i < src.length; i++) - dst[i] = data[0][src[i] - offset]; + for (int i = 0; i < src.length; i++) + dst[i] = data[0][src[i] - offset]; else - for (int i=0; i < src.length; i++) - dst[i] = data[i][src[i] - offset]; + for (int i = 0; i < src.length; i++) + dst[i] = data[i][src[i] - offset]; return dst; } @@ -142,6 +156,7 @@ public class ShortLookupTable extends LookupTable * @param src Component values of a pixel. * @param dst Destination array for values, or null. * @return Translated values for the pixel. + * */ public short[] lookupPixel(short[] src, short[] dst) throws ArrayIndexOutOfBoundsException @@ -150,11 +165,11 @@ public class ShortLookupTable extends LookupTable dst = new short[src.length]; if (data.length == 1) - for (int i=0; i < src.length; i++) - dst[i] = data[0][((int)src[i]) - offset]; + for (int i = 0; i < src.length; i++) + dst[i] = data[0][((int) src[i]) - offset]; else - for (int i=0; i < src.length; i++) - dst[i] = data[i][((int)src[i]) - offset]; + for (int i = 0; i < src.length; i++) + dst[i] = data[i][((int) src[i]) - offset]; return dst; diff --git a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java index 6ccce753bd3..a37fc0bba3f 100644 --- a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java +++ b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation +/* Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -36,10 +36,16 @@ exception statement from your version. */ package java.awt.image; +import java.util.Arrays; + import gnu.java.awt.BitMaskExtent; import gnu.java.awt.Buffers; /** + * A <code>SampleModel</code> used when all samples are stored in a single + * data element in the {@link DataBuffer}, and each data element contains + * samples for one pixel only. + * * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) */ public class SinglePixelPackedSampleModel extends SampleModel @@ -49,12 +55,32 @@ public class SinglePixelPackedSampleModel extends SampleModel private int[] bitOffsets; private int[] sampleSize; + /** + * Creates a new <code>SinglePixelPackedSampleModel</code>. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param bitMasks an array containing the bit mask used to extract the + * sample value for each band. + */ public SinglePixelPackedSampleModel(int dataType, int w, int h, int[] bitMasks) { this(dataType, w, h, w, bitMasks); } + /** + * Creates a new <code>SinglePixelPackedSampleModel</code>. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param scanlineStride the number of data elements between a pixel on one + * row and the corresponding pixel on the next row. + * @param bitMasks an array containing the bit mask used to extract the + * sample value for each band. + */ public SinglePixelPackedSampleModel(int dataType, int w, int h, int scanlineStride, int[] bitMasks) { @@ -67,7 +93,8 @@ public class SinglePixelPackedSampleModel extends SampleModel case DataBuffer.TYPE_INT: break; default: - throw new IllegalArgumentException("SinglePixelPackedSampleModel unsupported dataType"); + throw new IllegalArgumentException( + "SinglePixelPackedSampleModel unsupported dataType"); } this.scanlineStride = scanlineStride; @@ -77,19 +104,35 @@ public class SinglePixelPackedSampleModel extends SampleModel sampleSize = new int[numBands]; BitMaskExtent extent = new BitMaskExtent(); - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) { - extent.setMask(bitMasks[b]); - sampleSize[b] = extent.bitWidth; - bitOffsets[b] = extent.leastSignificantBit; + // the mask is an unsigned integer + long mask = bitMasks[b] & 0xFFFFFFFFL; + extent.setMask(mask); + sampleSize[b] = extent.bitWidth; + bitOffsets[b] = extent.leastSignificantBit; } } + /** + * Returns the number of data elements. + * + * @return <code>1</code>. + */ public int getNumDataElements() { return 1; } + /** + * Creates a new <code>SampleModel</code> that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ public SampleModel createCompatibleSampleModel(int w, int h) { /* FIXME: We can avoid recalculation of bit offsets and sample @@ -103,6 +146,8 @@ public class SinglePixelPackedSampleModel extends SampleModel * Creates a DataBuffer for holding pixel data in the format and * layout described by this SampleModel. The returned buffer will * consist of one single bank. + * + * @return The data buffer. */ public DataBuffer createDataBuffer() { @@ -116,17 +161,40 @@ public class SinglePixelPackedSampleModel extends SampleModel return Buffers.createBuffer(getDataType(), size); } - + /** + * Returns an array containing the size (in bits) for each band accessed by + * the <code>SampleModel</code>. + * + * @return An array. + * + * @see #getSampleSize(int) + */ public int[] getSampleSize() { - return sampleSize; + return (int[]) sampleSize.clone(); } + /** + * Returns the size (in bits) of the samples for the specified band. + * + * @param band the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * + * @return The sample size (in bits). + */ public int getSampleSize(int band) { return sampleSize[band]; } + /** + * Returns the index in the data buffer that stores the pixel at (x, y). + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @return The index in the data buffer that stores the pixel at (x, y). + */ public int getOffset(int x, int y) { return scanlineStride*y + x; @@ -142,20 +210,40 @@ public class SinglePixelPackedSampleModel extends SampleModel return bitMasks; } + /** + * Returns the number of data elements from a pixel in one row to the + * corresponding pixel in the next row. + * + * @return The scanline stride. + */ public int getScanlineStride() { return scanlineStride; } + /** + * Creates a new <code>SinglePixelPackedSampleModel</code> that accesses + * the specified subset of bands. + * + * @param bands an array containing band indices (<code>null</code> not + * permitted). + * + * @return A new sample model. + * + * @throws NullPointerException if <code>bands</code> is <code>null</code>. + * @throws RasterFormatException if <code>bands.length</code> is greater + * than the number of bands in this model. + */ public SampleModel createSubsetSampleModel(int[] bands) { - // FIXME: Is this the right way to interpret bands? + if (bands.length > numBands) + throw new RasterFormatException("Too many bands."); int numBands = bands.length; int[] bitMasks = new int[numBands]; - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) bitMasks[b] = this.bitMasks[bands[b]]; return new SinglePixelPackedSampleModel(dataType, width, height, @@ -174,16 +262,20 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * This is a more efficient implementation of the default implementation in the super - * class. - * @param x The x-coordinate of the pixel rectangle to store in <code>obj</code>. - * @param y The y-coordinate of the pixel rectangle to store in <code>obj</code>. + * This is a more efficient implementation of the default implementation in + * the super class. + * @param x The x-coordinate of the pixel rectangle to store in + * <code>obj</code>. + * @param y The y-coordinate of the pixel rectangle to store in + * <code>obj</code>. * @param w The width of the pixel rectangle to store in <code>obj</code>. * @param h The height of the pixel rectangle to store in <code>obj</code>. - * @param obj The primitive array to store the pixels into or null to force creation. + * @param obj The primitive array to store the pixels into or null to force + * creation. * @param data The DataBuffer that is the source of the pixel data. * @return The primitive array containing the pixel data. - * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + * @see java.awt.image.SampleModel#getDataElements(int, int, int, int, + * java.lang.Object, java.awt.image.DataBuffer) */ public Object getDataElements(int x, int y, int w, int h, Object obj, DataBuffer data) @@ -209,10 +301,11 @@ public class SinglePixelPackedSampleModel extends SampleModel // Seems like the only sensible thing to do. throw new ClassCastException(); } - if(x==0 && scanlineStride == w) + if(x == 0 && scanlineStride == w) { // The full width need to be copied therefore we can copy in one shot. - System.arraycopy(pixelData, scanlineStride*y + data.getOffset(), obj, 0, size); + System.arraycopy(pixelData, scanlineStride*y + data.getOffset(), obj, + 0, size); } else { @@ -229,32 +322,68 @@ public class SinglePixelPackedSampleModel extends SampleModel return obj; } - + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If <code>iArray</code> is not <code>null</code>, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { int offset = scanlineStride*y + x; if (iArray == null) iArray = new int[numBands]; int samples = data.getElem(offset); - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b]; return iArray; } + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If <code>iArray</code> is not <code>null</code>, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if <code>null</code>, a new array will be allocated). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int offset = scanlineStride*y + x; if (iArray == null) iArray = new int[numBands*w*h]; int outOffset = 0; - for (y=0; y<h; y++) + for (y = 0; y < h; y++) { int lineOffset = offset; - for (x=0; x<w; x++) + for (x = 0; x < w; x++) { int samples = data.getElem(lineOffset++); - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; } offset += scanlineStride; @@ -262,6 +391,20 @@ public class SinglePixelPackedSampleModel extends SampleModel return iArray; } + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param data the data buffer (<code>null</code> not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public int getSample(int x, int y, int b, DataBuffer data) { int offset = scanlineStride*y + x; @@ -270,16 +413,18 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * This method implements a more efficient way to set data elements than the default - * implementation of the super class. It sets the data elements line by line instead - * of pixel by pixel. + * This method implements a more efficient way to set data elements than the + * default implementation of the super class. It sets the data elements line + * by line instead of pixel by pixel. + * * @param x The x-coordinate of the data elements in <code>obj</code>. * @param y The y-coordinate of the data elements in <code>obj</code>. * @param w The width of the data elements in <code>obj</code>. * @param h The height of the data elements in <code>obj</code>. * @param obj The primitive array containing the data elements to set. * @param data The DataBuffer to store the data elements into. - * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, + * java.lang.Object, java.awt.image.DataBuffer) */ public void setDataElements(int x, int y, int w, int h, Object obj, DataBuffer data) @@ -373,12 +518,24 @@ public class SinglePixelPackedSampleModel extends SampleModel } } + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (<code>null</code> not permitted). + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if either <code>iArray</code> or + * <code>data</code> is <code>null</code>. + */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { int offset = scanlineStride*y + x; int samples = 0; - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b]; data.setElem(offset, samples); @@ -394,7 +551,8 @@ public class SinglePixelPackedSampleModel extends SampleModel * @param h The height of the pixel rectangle in <code>obj</code>. * @param iArray The primitive array containing the pixels to set. * @param data The DataBuffer to store the pixels into. - * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], java.awt.image.DataBuffer) + * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], + * java.awt.image.DataBuffer) */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) @@ -407,7 +565,7 @@ public class SinglePixelPackedSampleModel extends SampleModel for (int xx=x; xx<(x+w); xx++) { int samples = 0; - for (int b=0; b<numBands; b++) + for (int b = 0; b < numBands; b++) samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b]; data.setElem(0, offset, samples); inOffset += numBands; @@ -416,7 +574,19 @@ public class SinglePixelPackedSampleModel extends SampleModel } } - + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + * @param data the data buffer (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>data</code> is <code>null</code>. + */ public void setSample(int x, int y, int b, int s, DataBuffer data) { int offset = scanlineStride*y + x; @@ -428,6 +598,76 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** + * Tests this sample model for equality with an arbitrary object. This + * method returns <code>true</code> if and only if: + * <ul> + * <li><code>obj</code> is not <code>null</code>; + * <li><code>obj</code> is an instance of + * <code>SinglePixelPackedSampleModel</code>; + * <li>both models have the same: + * <ul> + * <li><code>dataType</code>; + * <li><code>width</code>; + * <li><code>height</code>; + * <li><code>numBands</code>; + * <li><code>scanlineStride</code>; + * <li><code>bitMasks</code>; + * <li><code>bitOffsets</code>. + * </ul> + * </li> + * </ul> + * + * @param obj the object (<code>null</code> permitted) + * + * @return <code>true</code> if this model is equal to <code>obj</code>, and + * <code>false</code> otherwise. + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof SinglePixelPackedSampleModel)) + return false; + SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel) obj; + if (this.dataType != that.dataType) + return false; + if (this.width != that.width) + return false; + if (this.height != that.height) + return false; + if (this.numBands != that.numBands) + return false; + if (this.scanlineStride != that.scanlineStride) + return false; + if (!Arrays.equals(this.bitMasks, that.bitMasks)) + return false; + if (!Arrays.equals(this.bitOffsets, that.bitOffsets)) + return false; + return true; + } + + /** + * Returns a hash code for this <code>SinglePixelPackedSampleModel</code>. + * + * @return A hash code. + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + result = 37 * result + dataType; + result = 37 * result + width; + result = 37 * result + height; + result = 37 * result + numBands; + result = 37 * result + scanlineStride; + for (int i = 0; i < bitMasks.length; i++) + result = 37 * result + bitMasks[i]; + for (int i = 0; i < bitOffsets.length; i++) + result = 37 * result + bitOffsets[i]; + return result; + } + + /** * Creates a String with some information about this SampleModel. * @return A String describing this SampleModel. * @see java.lang.Object#toString() @@ -438,9 +678,10 @@ public class SinglePixelPackedSampleModel extends SampleModel result.append(getClass().getName()); result.append("["); result.append("scanlineStride=").append(scanlineStride); - for(int i=0; i < bitMasks.length; i+=1) + for(int i = 0; i < bitMasks.length; i+=1) { - result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + result.append(", mask[").append(i).append("]=0x").append( + Integer.toHexString(bitMasks[i])); } result.append("]"); diff --git a/libjava/classpath/java/awt/image/WritableRaster.java b/libjava/classpath/java/awt/image/WritableRaster.java index 2e5462fd92e..473c6fe41f9 100644 --- a/libjava/classpath/java/awt/image/WritableRaster.java +++ b/libjava/classpath/java/awt/image/WritableRaster.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2000, 2002, 2003 Free Software Foundation +/* Copyright (C) 2000, 2002, 2003, 2006, Free Software Foundation This file is part of GNU Classpath. @@ -41,61 +41,98 @@ import java.awt.Point; import java.awt.Rectangle; /** + * A raster with methods to support updating pixel values. + * * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) */ public class WritableRaster extends Raster { + /** + * Creates a new <code>WritableRaster</code>. + * + * @param sampleModel the sample model. + * @param origin the origin. + */ protected WritableRaster(SampleModel sampleModel, Point origin) { this(sampleModel, sampleModel.createDataBuffer(), origin); } - protected WritableRaster(SampleModel sampleModel, - DataBuffer dataBuffer, Point origin) + /** + * Creates a new <code>WritableRaster</code> instance. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param origin the origin. + */ + protected WritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, + Point origin) { this(sampleModel, dataBuffer, - new Rectangle(origin != null ? origin.x : 0, + new Rectangle(origin != null ? origin.x : 0, origin != null ? origin.y : 0, - sampleModel.getWidth(), sampleModel.getHeight()), - origin, - null); + sampleModel.getWidth(), sampleModel.getHeight()), + origin, null); } + /** + * Creates a new <code>WritableRaster</code> instance. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param aRegion the raster's bounds. + * @param sampleModelTranslate the translation. + * @param parent the parent. + */ protected WritableRaster(SampleModel sampleModel, - DataBuffer dataBuffer, - Rectangle aRegion, - Point sampleModelTranslate, - WritableRaster parent) + DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, + WritableRaster parent) { - super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, - parent); + super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, parent); } + /** + * Returns the raster's parent, cast as a {@link WritableRaster}. + * + * @return The raster's parent. + */ public WritableRaster getWritableParent() { return (WritableRaster) getParent(); } + /** + * @param childMinX + * @param childMinY + * @return + */ public WritableRaster createWritableTranslatedChild(int childMinX, - int childMinY) + int childMinY) { // This mirrors the code from the super class int tcx = sampleModelTranslateX - minX + childMinX; int tcy = sampleModelTranslateY - minY + childMinY; return new WritableRaster(sampleModel, dataBuffer, - new Rectangle(childMinX, childMinY, - width, height), - new Point(tcx, tcy), - this); + new Rectangle(childMinX, childMinY, width, height), + new Point(tcx, tcy), this); } - public WritableRaster createWritableChild(int parentX, - int parentY, - int w, int h, - int childMinX, - int childMinY, - int[] bandList) + /** + * + * @param parentX + * @param parentY + * @param w + * @param h + * @param childMinX + * @param childMinY + * @param bandList + * @return + */ + public WritableRaster createWritableChild(int parentX, int parentY, + int w, int h, int childMinX, int childMinY, int[] bandList) { // This mirrors the code from the super class @@ -106,51 +143,52 @@ public class WritableRaster extends Raster sampleModel : sampleModel.createSubsetSampleModel(bandList); - return new - WritableRaster(sm, dataBuffer, - new Rectangle(childMinX, childMinY, - w, h), - new Point(sampleModelTranslateX+childMinX-parentX, - sampleModelTranslateY+childMinY-parentY), - this); + return new WritableRaster(sm, dataBuffer, + new Rectangle(childMinX, childMinY, w, h), + new Point(sampleModelTranslateX + childMinX - parentX, + sampleModelTranslateY + childMinY - parentY), + this); } public void setDataElements(int x, int y, Object inData) { - sampleModel.setDataElements(x-sampleModelTranslateX, - y-sampleModelTranslateY, - inData, dataBuffer); + sampleModel.setDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, inData, dataBuffer); } public void setDataElements(int x, int y, Raster inRaster) { - Object dataElements = getDataElements(0, 0, - inRaster.getWidth(), - inRaster.getHeight(), - null); + Object dataElements = getDataElements(0, 0, inRaster.getWidth(), + inRaster.getHeight(), null); setDataElements(x, y, dataElements); } - public void setDataElements(int x, int y, int w, int h, - Object inData) + public void setDataElements(int x, int y, int w, int h, Object inData) { - sampleModel.setDataElements(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, inData, dataBuffer); + sampleModel.setDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, inData, dataBuffer); } + /** + * + * @param srcRaster + */ public void setRect(Raster srcRaster) { setRect(0, 0, srcRaster); } + /** + * + * @param dx + * @param dy + * @param srcRaster + */ public void setRect(int dx, int dy, Raster srcRaster) { - Rectangle targetUnclipped = new Rectangle(srcRaster.getMinX()+dx, - srcRaster.getMinY()+dy, - srcRaster.getWidth(), - srcRaster.getHeight()); - + Rectangle targetUnclipped = new Rectangle(srcRaster.getMinX() + dx, + srcRaster.getMinY() + dy, srcRaster.getWidth(), srcRaster.getHeight()); + Rectangle target = getBounds().intersection(targetUnclipped); if (target.isEmpty()) return; @@ -169,97 +207,225 @@ public class WritableRaster extends Raster But this is probably not the place to consider such optimizations.*/ - int[] pixels = srcRaster.getPixels(sx, sy, - target.width, target.height, - (int[]) null); + int[] pixels = srcRaster.getPixels(sx, sy, target.width, target.height, + (int[]) null); setPixels(target.x, target.y, target.width, target.height, pixels); } + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>iArray</code> is <code>null</code>. + */ public void setPixel(int x, int y, int[] iArray) { - sampleModel.setPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - iArray, dataBuffer); + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + iArray, dataBuffer); } + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>fArray</code> is <code>null</code>. + */ public void setPixel(int x, int y, float[] fArray) { - sampleModel.setPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - fArray, dataBuffer); + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + fArray, dataBuffer); } + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>dArray</code> is <code>null</code>. + */ public void setPixel(int x, int y, double[] dArray) { - sampleModel.setPixel(x-sampleModelTranslateX, - y-sampleModelTranslateY, - dArray, dataBuffer); + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + dArray, dataBuffer); } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>iArray</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, int[] iArray) { - sampleModel.setPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, iArray, dataBuffer); + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, iArray, dataBuffer); } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray the pixel sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>fArray</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, float[] fArray) { - sampleModel.setPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, fArray, dataBuffer); + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, fArray, dataBuffer); } + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray the pixel sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>dArray</code> is <code>null</code>. + */ public void setPixels(int x, int y, int w, int h, double[] dArray) { - sampleModel.setPixels(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, dArray, dataBuffer); + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, dArray, dataBuffer); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + */ public void setSample(int x, int y, int b, int s) { - sampleModel.setSample(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, s, dataBuffer); + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + */ public void setSample(int x, int y, int b, float s) { - sampleModel.setSample(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, s, dataBuffer); + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); } + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range <code>0</code> to + * <code>getNumBands() - 1</code>). + * @param s the sample value. + */ public void setSample(int x, int y, int b, double s) { - sampleModel.setSample(x-sampleModelTranslateX, - y-sampleModelTranslateY, - b, s, dataBuffer); + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param iArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>iArray</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, - int[] iArray) + int[] iArray) { - sampleModel.setSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, iArray, dataBuffer); + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, iArray, dataBuffer); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param fArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>fArray</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, - float[] fArray) + float[] fArray) { - sampleModel.setSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, fArray, dataBuffer); + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, fArray, dataBuffer); } + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range <code>0</code> to + * </code>getNumBands() - 1</code>). + * @param dArray the sample values (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>dArray</code> is <code>null</code>. + */ public void setSamples(int x, int y, int w, int h, int b, - double[] dArray) + double[] dArray) { - sampleModel.setSamples(x-sampleModelTranslateX, - y-sampleModelTranslateY, - w, h, b, dArray, dataBuffer); + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, dArray, dataBuffer); } } diff --git a/libjava/classpath/java/awt/peer/ComponentPeer.java b/libjava/classpath/java/awt/peer/ComponentPeer.java index 97b96f49cdb..bc6e3a457f3 100644 --- a/libjava/classpath/java/awt/peer/ComponentPeer.java +++ b/libjava/classpath/java/awt/peer/ComponentPeer.java @@ -153,6 +153,12 @@ public interface ComponentPeer * {@link Component#getMinimumSize()}. * * @return the minimum size for the component + * + * @specnote Presumably this method got added to replace minimumSize(). + * However, testing shows that this is never called in the RI + * (tested with JDK5), but instead minimumSize() is called + * directly. It is advisable to implement this method to delegate + * to minimumSize() and put the real implementation in there. */ Dimension getMinimumSize(); @@ -161,6 +167,12 @@ public interface ComponentPeer * {@link Component#getPreferredSize()}. * * @return the preferred size for the component + * + * @specnote Presumably this method got added to replace preferredSize(). + * However, testing shows that this is never called in the RI + * (tested with JDK5), but instead preferredSize() is called + * directly. It is advisable to implement this method to delegate + * to preferredSize() and put the real implementation in there. */ Dimension getPreferredSize(); @@ -262,12 +274,21 @@ public interface ComponentPeer * Requests that this component receives the focus. This is called from * {@link Component#requestFocus()}. * - * @param source TODO - * @param bool1 TODO - * @param bool2 TODO - * @param x TODO - */ - boolean requestFocus(Component source, boolean bool1, boolean bool2, long x); + * This method is only called for heavyweight component's peers. Lightweight + * components ask their nearest heavyweight component to request focus. + * It's up to the heavyweight peer to decide if any of it's lightweight + * descendants are allowed to receive keyboard input focus or not. If the + * focus request is finally approved, then the peer must post a FOCUS_GAINED + * event for the requested component. + * + * @param request the component for which the focus is requested + * @param temporary indicates if the focus change is temporary (true) or + * permanent (false) + * @param allowWindowFocus indicates if it's allowed to change window focus + * @param time the timestamp + */ + boolean requestFocus(Component request, boolean temporary, + boolean allowWindowFocus, long time); /** * Notifies the peer that the bounds of this component have changed. This diff --git a/libjava/classpath/java/awt/peer/MouseInfoPeer.java b/libjava/classpath/java/awt/peer/MouseInfoPeer.java new file mode 100644 index 00000000000..e9923a653aa --- /dev/null +++ b/libjava/classpath/java/awt/peer/MouseInfoPeer.java @@ -0,0 +1,61 @@ +/* MouseInfoPeer.java -- peer interface for MouseInfo + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Point; +import java.awt.Window; + +/** + * MouseInfoPeer is the peer interface java.awt.MouseInfo. + * + * @author Sven de Marothy + * @since 1.5 + */ +public interface MouseInfoPeer +{ + /** + * Get the mouse pointer coordinates and store them in p (obviously non-null) + * returns the index of the current screen device of the mouse. + */ + public int fillPointWithCoords(Point p); + + /** + * Returns whether a given Window is under the mouse. + */ + public boolean isWindowUnderMouse(Window w); +} diff --git a/libjava/classpath/java/awt/peer/WindowPeer.java b/libjava/classpath/java/awt/peer/WindowPeer.java index 6c014de0b99..00d1035791a 100644 --- a/libjava/classpath/java/awt/peer/WindowPeer.java +++ b/libjava/classpath/java/awt/peer/WindowPeer.java @@ -1,5 +1,5 @@ /* WindowPeer.java -- Interface for window peers - Copyright (C) 1999 Free Software Foundation, Inc. + Copyright (C) 1999, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -44,7 +44,8 @@ public interface WindowPeer extends ContainerPeer void toFront(); /** - * FIXME: unknown. + * Update the always-on-top status of the Window. + * * @since 1.5 */ void updateAlwaysOnTop(); diff --git a/libjava/classpath/java/beans/VetoableChangeSupport.java b/libjava/classpath/java/beans/VetoableChangeSupport.java index dce8dffd341..12051d2ffb0 100644 --- a/libjava/classpath/java/beans/VetoableChangeSupport.java +++ b/libjava/classpath/java/beans/VetoableChangeSupport.java @@ -1,5 +1,6 @@ /* VetoableChangeSupport.java -- support to manage vetoable change listeners - Copyright (C) 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006, + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -120,14 +121,15 @@ public class VetoableChangeSupport implements Serializable * vetoable change events will be sent to this listener. The listener add * is not unique: that is, <em>n</em> adds with the same listener will * result in <em>n</em> events being sent to that listener for every - * vetoable change. Adding a null listener may cause a NullPointerException - * down the road. This method will unwrap a VetoableChangeListenerProxy, + * vetoable change. This method will unwrap a VetoableChangeListenerProxy, * registering the underlying delegate to the named property list. * - * @param l the listener to add + * @param l the listener to add (<code>null</code> ignored). */ public synchronized void addVetoableChangeListener(VetoableChangeListener l) { + if (l == null) + return; if (l instanceof VetoableChangeListenerProxy) { VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; @@ -215,19 +217,19 @@ public class VetoableChangeSupport implements Serializable * being sent to that listener when that property is changed. The effect is * cumulative, too; if you are registered to listen to receive events on * all vetoable changes, and then you register on a particular property, - * you will receive change events for that property twice. Adding a null - * listener may cause a NullPointerException down the road. This method + * you will receive change events for that property twice. This method * will unwrap a VetoableChangeListenerProxy, registering the underlying * delegate to the named property list if the names match, and discarding * it otherwise. * * @param propertyName the name of the property to listen on * @param l the listener to add - * @throws NullPointerException if propertyName is null */ public synchronized void addVetoableChangeListener(String propertyName, VetoableChangeListener l) { + if (propertyName == null || l == null) + return; while (l instanceof VetoableChangeListenerProxy) { VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; diff --git a/libjava/classpath/java/beans/beancontext/BeanContextSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java index f3d5ff6098d..a12c078df6a 100644 --- a/libjava/classpath/java/beans/beancontext/BeanContextSupport.java +++ b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java @@ -1,5 +1,5 @@ /* BeanContextSupport.java -- - Copyright (C) 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,6 +40,7 @@ package java.beans.beancontext; import gnu.classpath.NotImplementedException; +import java.beans.Beans; import java.beans.DesignMode; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -59,7 +60,12 @@ import java.util.Iterator; import java.util.Locale; /** + * This is a helper class for implementing a bean context. It is + * intended to be used either by subclassing or by calling methods + * of this implementation from another. + * * @author Michael Koch + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @since 1.2 */ public class BeanContextSupport extends BeanContextChildSupport @@ -180,27 +186,102 @@ public class BeanContextSupport extends BeanContextChildSupport initialize (); } - public boolean add (Object targetChild) + /** + * <p> + * Add a child to the bean context. A child can be a simple + * <code>Object</code>, a <code>BeanContextChild</code> + * or another <code>BeanContext</code>. + * </p> + * <p> + * The children of a <code>BeanContext</code> form a set. As + * a result, this method returns <code>false</code> if the given + * object is already a child of this context. + * </p> + * <p> + * If the child is a <code>BeanContextChild</code>, or a proxy + * for such a child, the <code>setBeanContext()</code> method + * is invoked on the child. If this operation is vetoed by the + * child, via throwing a <code>PropertyVetoException</code>, + * then the current completion state of the <code>add()</code> + * operation is rolled back and a <code>IllegalStateException</code> + * is thrown. If the <code>BeanContextChild</code> is successfully + * added, then the context registers with its + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> for "beanContext" events. + * </p> + * <p> + * If the child implements <code>java.beans.Visibility</code>, + * then its ability to use a GUI is set based on that of + * this context. + * </p> + * <p> + * A <code>BeanContextMembershipEvent</code> is fired when the + * child is successfully added to the bean context. + * </p> + * <p> + * This method is synchronized over the global hierarchy lock. + * </p> + * + * @param targetChild the child to add. + * @return false if the child has already been added. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ + public boolean add(Object targetChild) { - if (targetChild == null) - throw new IllegalArgumentException(); - - BCSChild child; - synchronized (children) - { - if (children.containsKey(targetChild) - || ! validatePendingAdd(targetChild)) - return false; - child = createBCSChild(targetChild, beanContextChildPeer); - children.put(targetChild, child); - } - synchronized (targetChild) + synchronized (globalHierarchyLock) { - childJustAddedHook(targetChild, child); + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (children.containsKey(targetChild) + || ! validatePendingAdd(targetChild)) + return false; + child = createBCSChild(targetChild, beanContextChildPeer); + children.put(targetChild, child); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + bcChild.setBeanContext(this); + bcChild.addVetoableChangeListener("beanContext", this); + bcChild.addPropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.remove(targetChild); + } + throw new IllegalStateException("The child refused to " + + "associate itself with " + + "this context.", e); + } + if (targetChild instanceof Visibility) + { + Visibility visibleChild = (Visibility) targetChild; + if (okToUseGui) + visibleChild.okToUseGui(); + else + visibleChild.dontUseGui(); + } + childJustAddedHook(targetChild, child); + } + fireChildrenAdded(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; } - fireChildrenAdded(new BeanContextMembershipEvent(this, - new Object[] { targetChild })); - return true; } public boolean addAll (Collection c) @@ -219,10 +300,18 @@ public class BeanContextSupport extends BeanContextChildSupport } } - public boolean avoidingGui () + /** + * Returns true if this bean needs a GUI + * but is being prevented from using one. + * + * @return true if <code>needsGui()</code> + * is true but the bean has been + * told not to use it. + */ + public boolean avoidingGui() throws NotImplementedException { - throw new Error ("Not implemented"); + return needsGui() && (!okToUseGui); } protected Iterator bcsChildren () @@ -321,10 +410,13 @@ public class BeanContextSupport extends BeanContextChildSupport throw new Error ("Not implemented"); } - public void dontUseGui () - throws NotImplementedException + /** + * Informs this bean that is should not make + * use of the GUI. + */ + public void dontUseGui() { - throw new Error ("Not implemented"); + okToUseGui = false; } protected final void fireChildrenAdded (BeanContextMembershipEvent bcme) @@ -426,10 +518,20 @@ public class BeanContextSupport extends BeanContextChildSupport children = new HashMap(); } + /** + * This is a convenience method for instantiating a bean inside this + * context. It delegates to the appropriate method in + * <code>java.beans.Beans</code> using the context's classloader. + * + * @param beanName the name of the class of bean to instantiate. + * @throws IOException if an I/O error occurs in loading the class. + * @throws ClassNotFoundException if the class, <code>beanName</code>, + * can not be found. + */ public Object instantiateChild (String beanName) - throws IOException, ClassNotFoundException, NotImplementedException + throws IOException, ClassNotFoundException { - throw new Error ("Not implemented"); + return Beans.instantiate(getClass().getClassLoader(), beanName, this); } public boolean isDesignTime () @@ -437,6 +539,11 @@ public class BeanContextSupport extends BeanContextChildSupport return designTime; } + /** + * Returns true if this bean context has no children. + * + * @return true if there are no children. + */ public boolean isEmpty () { synchronized (children) @@ -459,22 +566,38 @@ public class BeanContextSupport extends BeanContextChildSupport } } - public boolean needsGui () - throws NotImplementedException + /** + * Returns false as this bean does not a + * GUI for its operation. + * + * @return false + */ + public boolean needsGui() { - throw new Error ("Not implemented"); + return false; } + /** + * Informs this bean that it is okay to make use of + * the GUI. + */ public void okToUseGui () - throws NotImplementedException { - throw new Error ("Not implemented"); + okToUseGui = true; } + /** + * Subclasses may use this method to catch property changes + * arising from the children of this context. At present, + * we just listen for the beans being assigned to a different + * context and remove them from here if such an event occurs. + * + * @param pce the property change event. + */ public void propertyChange (PropertyChangeEvent pce) - throws NotImplementedException { - throw new Error ("Not implemented"); + if (pce.getNewValue() != this) + remove(pce.getSource(), false); } public final void readChildren (ObjectInputStream ois) @@ -483,18 +606,100 @@ public class BeanContextSupport extends BeanContextChildSupport throw new Error ("Not implemented"); } + /** + * Remove the specified child from the context. This is + * the same as calling <code>remove(Object,boolean)</code> + * with a request for the <code>setBeanContext()</code> method + * of the child to be called (i.e. the second argument is true). + * + * @param targetChild the child to remove. + */ public boolean remove (Object targetChild) { return remove(targetChild, true); } + /** + * <p> + * Removes a child from the bean context. A child can be a simple + * <code>Object</code>, a <code>BeanContextChild</code> + * or another <code>BeanContext</code>. If the given child is not + * a child of this context, this method returns <code>false</code>. + * </p> + * <p> + * If the child is a <code>BeanContextChild</code>, or a proxy + * for such a child, the <code>setBeanContext()</code> method + * is invoked on the child (if specified). If this operation is vetoed + * by the child, via throwing a <code>PropertyVetoException</code>, + * then the current completion state of the <code>remove()</code> + * operation is rolled back and a <code>IllegalStateException</code> + * is thrown. If the <code>BeanContextChild</code> is successfully + * removed, then the context deregisters with its + * <code>PropertyChangeListener</code> and + * <code>VetoableChangeListener</code> for "beanContext" events. + * </p> + * <p> + * A <code>BeanContextMembershipEvent</code> is fired when the + * child is successfully removed from the bean context. + * </p> + * <p> + * This method is synchronized over the global hierarchy lock. + * </p> + * + * @param targetChild the child to add. + * @param callChildSetBC true if the <code>setBeanContext()</code> + * method of the child should be called. + * @return false if the child doesn't exist. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ protected boolean remove (Object targetChild, boolean callChildSetBC) - throws NotImplementedException { - if (targetChild == null) - throw new IllegalArgumentException(); - - throw new Error ("Not implemented"); + synchronized (globalHierarchyLock) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (!children.containsKey(targetChild) + || !validatePendingRemove(targetChild)) + return false; + child = (BCSChild) children.remove(targetChild); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + if (callChildSetBC) + bcChild.setBeanContext(null); + bcChild.removeVetoableChangeListener("beanContext", this); + bcChild.removePropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.put(targetChild, child); + } + throw new IllegalStateException("The child refused to " + + "disassociate itself with " + + "this context.", e); + } + childJustRemovedHook(targetChild, child); + } + fireChildrenRemoved(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; + } } public boolean removeAll (Collection c) @@ -578,10 +783,16 @@ public class BeanContextSupport extends BeanContextChildSupport return true; } + /** + * Subclasses may use this method to veto changes arising + * from the children of this context. + * + * @param pce the vetoable property change event fired. + */ public void vetoableChange (PropertyChangeEvent pce) - throws PropertyVetoException, NotImplementedException + throws PropertyVetoException { - throw new Error ("Not implemented"); + /* Purposefully left empty */ } public final void writeChildren (ObjectOutputStream oos) diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java index 43e8e5ded6c..ce56876cc22 100644 --- a/libjava/classpath/java/io/File.java +++ b/libjava/classpath/java/io/File.java @@ -1,5 +1,5 @@ /* File.java -- Class representing a file on disk - Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -82,7 +82,7 @@ public class File implements Serializable, Comparable /** * This is the string that is used to separate the host name from the - * path name in paths than include the host name. It is the value of + * path name in paths that include the host name. It is the value of * the <code>path.separator</code> system property. */ public static final String pathSeparator @@ -484,9 +484,9 @@ public class File implements Serializable, Comparable /** * This method returns a canonical representation of the pathname of * this file. The actual form of the canonical representation is - * different. On the GNU system, the canonical form differs from the - * absolute form in that all relative file references to "." and ".." - * are resolved and removed. + * system-dependent. On the GNU system, conversion to canonical + * form involves the removal of redundant separators, references to + * "." and "..", and symbolic links. * <p> * Note that this method, unlike the other methods which return path * names, can throw an IOException. This is because native method @@ -542,7 +542,8 @@ public class File implements Serializable, Comparable /** * This method returns a <code>String</code> the represents this file's * parent. <code>null</code> is returned if the file has no parent. The - * parent is determined via a simple operation which removes the + * parent is determined via a simple operation which removes the name + * after the last file separator character, as determined by the platform. * * @return The parent directory of this file */ @@ -1199,7 +1200,38 @@ public class File implements Serializable, Comparable */ public static File[] listRoots() { - return VMFile.listRoots(); + File[] roots = VMFile.listRoots(); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + // Only return roots to which the security manager permits read access. + int count = roots.length; + for (int i = 0; i < roots.length; i++) + { + try + { + s.checkRead (roots[i].path); + } + catch (SecurityException sx) + { + roots[i] = null; + count--; + } + } + if (count != roots.length) + { + File[] newRoots = new File[count]; + int k = 0; + for (int i = 0; i < roots.length; i++) + { + if (roots[i] != null) + newRoots[k++] = roots[i]; + } + roots = newRoots; + } + } + return roots; } /** diff --git a/libjava/classpath/java/io/FilePermission.java b/libjava/classpath/java/io/FilePermission.java index 9a83efba621..0872a378bc3 100644 --- a/libjava/classpath/java/io/FilePermission.java +++ b/libjava/classpath/java/io/FilePermission.java @@ -274,12 +274,7 @@ public final class FilePermission extends Permission implements Serializable break; default: - if (f2.charAt(f2.length() - 1) == File.separatorChar) - { - if (! f1.equals(f2.substring(0, f2.length() - 1))) - return false; - } - else if (!f1.equals(f2)) + if (!f1.equals(f2)) return false; break; } diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java index 91832f94e2c..a37ad73fd86 100644 --- a/libjava/classpath/java/io/ObjectInputStream.java +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -424,7 +424,23 @@ public class ObjectInputStream extends InputStream clearHandles(); throw new WriteAbortedException("Exception thrown during writing of stream", e); } - + + case TC_ENUM: + { + /* TC_ENUM classDesc newHandle enumConstantName */ + if (dump) + dumpElementln("ENUM="); + ObjectStreamClass osc = (ObjectStreamClass) readObject(); + String constantName = (String) readObject(); + if (dump) + dumpElementln("CONSTANT NAME = " + constantName); + Class clazz = osc.forClass(); + Enum instance = Enum.valueOf(clazz, constantName); + assignNewHandle(instance); + ret_val = instance; + break; + } + default: throw new IOException("Unknown marker on stream: " + marker); } diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java index 61f07bc7cfb..80d196bce1b 100644 --- a/libjava/classpath/java/io/ObjectOutputStream.java +++ b/libjava/classpath/java/io/ObjectOutputStream.java @@ -253,7 +253,17 @@ public class ObjectOutputStream extends OutputStream ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz); if (osc == null) throw new NotSerializableException(clazz.getName()); - + + if (osc.isEnum()) + { + /* TC_ENUM classDesc newHandle enumConstantName */ + realOutput.writeByte(TC_ENUM); + writeObject(osc); + assignNewHandle(obj); + writeObject(((Enum) obj).name()); + break; + } + if ((replacementEnabled || obj instanceof Serializable) && ! replaceDone) { @@ -432,7 +442,10 @@ public class ObjectOutputStream extends OutputStream { realOutput.writeByte(TC_CLASSDESC); realOutput.writeUTF(osc.getName()); - realOutput.writeLong(osc.getSerialVersionUID()); + if (osc.isEnum()) + realOutput.writeLong(0L); + else + realOutput.writeLong(osc.getSerialVersionUID()); assignNewHandle(osc); int flags = osc.getFlags(); diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java index 203e4a5abaa..abb26d839ad 100644 --- a/libjava/classpath/java/io/ObjectStreamClass.java +++ b/libjava/classpath/java/io/ObjectStreamClass.java @@ -219,6 +219,12 @@ public class ObjectStreamClass implements Serializable return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; } + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isEnum() + { + return (flags & ObjectStreamConstants.SC_ENUM) != 0; + } // Returns the <code>ObjectStreamClass</code> that represents the // class that is the superclass of the class this @@ -587,6 +593,9 @@ outer: if (writeObjectMethod != null) flags |= ObjectStreamConstants.SC_WRITE_METHOD; + + if (cl.isEnum() || cl == Enum.class) + flags |= ObjectStreamConstants.SC_ENUM; } @@ -596,7 +605,7 @@ outer: { SetAccessibleAction setAccessible = new SetAccessibleAction(); - if (!isSerializable() || isExternalizable()) + if (!isSerializable() || isExternalizable() || isEnum()) { fields = NO_FIELDS; return; diff --git a/libjava/classpath/java/io/ObjectStreamConstants.java b/libjava/classpath/java/io/ObjectStreamConstants.java index 04cf79bed3e..a29db2a0e2c 100644 --- a/libjava/classpath/java/io/ObjectStreamConstants.java +++ b/libjava/classpath/java/io/ObjectStreamConstants.java @@ -50,8 +50,6 @@ package java.io; */ public interface ObjectStreamConstants { - // FIXME: Javadoc comment these values. - /** * The serialization stream protocol version 1. This version was * the default serialization protocol before JDK 1.2. @@ -70,37 +68,159 @@ public interface ObjectStreamConstants */ int PROTOCOL_VERSION_2 = 2; + /** + * The magic number that is written as part of the stream header. + */ short STREAM_MAGIC = (short)0xaced; + + /** + * The stream version number that is written as part of the stream header. + * Note that this is different from the protocol version that specifies + * the data format for the stream. + */ short STREAM_VERSION = 5; + /** + * Token value to designate a <code>null</code> reference in the stream. + */ byte TC_NULL = (byte)112; //0x70 + + /** + * Token value to designate a reference to an already serialized object. + */ byte TC_REFERENCE = (byte)113; //0x71 + + /** + * Token value to designate a class descriptor is next in the stream. + */ byte TC_CLASSDESC = (byte)114; //0x72 + + /** + * Token value to designate a new object is next in the stream. + */ byte TC_OBJECT = (byte)115; //0x73 + + /** + * Token value to designate a new string is next in the stream. + */ byte TC_STRING = (byte)116; //0x74 + + /** + * Token value to designate a new array is next in the stream. + */ byte TC_ARRAY = (byte)117; //0x75 + + /** + * Token reference to designate a reference to a class. + */ byte TC_CLASS = (byte)118; //0x76 + + /** + * Token value to designate a block of primitive data is next in the stream. + * The next byte in the stream holds the size of the block (in bytes). + */ byte TC_BLOCKDATA = (byte)119; //0x77 + + /** + * Token value to designate the end of a block of primitve data. + */ byte TC_ENDBLOCKDATA = (byte)120; //0x78 + + /** + * Token value to designate a reset of the stream state. + */ byte TC_RESET = (byte)121; //0x79 + + /** + * Token value to designate a long block of primitive data is next in the + * stream. The next long in the stream holds the size of the block + * (in bytes). + */ byte TC_BLOCKDATALONG = (byte)122; //0x7A + + /** + * Token value to designate an exception occured during serialization. + */ byte TC_EXCEPTION = (byte)123; //0x7B + + /** + * Token value to designate a long string is next in the stream. + */ byte TC_LONGSTRING = (byte)124; //0x7C + + /** + * Token value to designate a proxy class descriptor is next in the stream. + */ byte TC_PROXYCLASSDESC = (byte)125; //0x7D + /** + * Token value to designate an enum constant is next in the stream. + * + * @since 1.5 + */ + byte TC_ENUM = (byte)126; //0x7E + + /** + * The first token value. + */ byte TC_BASE = TC_NULL; - byte TC_MAX = TC_PROXYCLASSDESC; + + /** + * The last token value. + */ + byte TC_MAX = TC_ENUM; + /** + * The first handle that will be assigned to an object, for later references. + */ int baseWireHandle = 0x7e0000; + /** + * Flag used in <code>ObjectStreamClass</code> to designate that the class + * defines the <code>writeObject</code> method. + */ byte SC_WRITE_METHOD = 0x01; + + /** + * Flag used in <code>ObjectStreamClass</code> to designate that the class + * is serializeable. + */ byte SC_SERIALIZABLE = 0x02; + + /** + * Flag used in <code>ObjectStreamClass</code> to designate that the class + * is externalizable. + */ byte SC_EXTERNALIZABLE = 0x04; + + /** + * Flag used in <code>ObjectStreamClass</code> to designate that + * externalizable data is written in block data mode. + * + * @since 1.2 + */ byte SC_BLOCK_DATA = 0x08; + /** + * Flag used in <code>ObjectStreamClass</code> to designate that the class + * is an enum constant. + * + * @since 1.5 + */ + byte SC_ENUM = 0x10; + + /** + * Constant for use with a <code>SecurityManager</code> to check if + * substitution of objects is allowed. + */ SerializablePermission SUBSTITUTION_PERMISSION = new SerializablePermission("enableSubstitution"); + /** + * Constant for use with a <code>SecurityManager</code> to check if + * overriding of the <code>writeObject</code> and <code>readObject</code> + * methods is allowed. + */ SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new SerializablePermission("enableSubclassImplementation"); } diff --git a/libjava/classpath/java/io/OutputStream.java b/libjava/classpath/java/io/OutputStream.java index 8608daaa161..e3caa70f19c 100644 --- a/libjava/classpath/java/io/OutputStream.java +++ b/libjava/classpath/java/io/OutputStream.java @@ -1,5 +1,5 @@ /* OutputStream.java -- Base class for byte output streams - Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -48,7 +48,7 @@ package java.io; * @author Aaron M. Renn (arenn@urbanophile.com) * @author Tom Tromey (tromey@cygnus.com) */ -public abstract class OutputStream +public abstract class OutputStream implements Closeable, Flushable { /** * This is the default no-argument constructor for this class. This method diff --git a/libjava/classpath/java/io/PrintStream.java b/libjava/classpath/java/io/PrintStream.java index 99af25583b0..98461db2ba8 100644 --- a/libjava/classpath/java/io/PrintStream.java +++ b/libjava/classpath/java/io/PrintStream.java @@ -87,8 +87,76 @@ public class PrintStream extends FilterOutputStream private boolean auto_flush; /** - * This method intializes a new <code>PrintStream</code> object to write - * to the specified output sink. + * This method initializes a new <code>PrintStream</code> object to write + * to the specified output File. Doesn't autoflush. + * + * @param file The <code>File</code> to write to. + * @throws FileNotFoundException if an error occurs while opening the file. + * + * @since 1.5 + */ + public PrintStream (File file) + throws FileNotFoundException + { + this (new FileOutputStream(file), false); + } + + /** + * This method initializes a new <code>PrintStream</code> object to write + * to the specified output File. Doesn't autoflush. + * + * @param file The <code>File</code> to write to. + * @param encoding The name of the character encoding to use for this + * object. + * @throws FileNotFoundException If an error occurs while opening the file. + * @throws UnsupportedEncodingException If the charset specified by + * <code>encoding</code> is invalid. + * + * @since 1.5 + */ + public PrintStream (File file, String encoding) + throws FileNotFoundException,UnsupportedEncodingException + { + this (new FileOutputStream(file), false, encoding); + } + + /** + * This method initializes a new <code>PrintStream</code> object to write + * to the specified output File. Doesn't autoflush. + * + * @param fileName The name of the <code>File</code> to write to. + * @throws FileNotFoundException if an error occurs while opening the file, + * + * @since 1.5 + */ + public PrintStream (String fileName) + throws FileNotFoundException + { + this (new FileOutputStream(new File(fileName)), false); + } + + /** + * This method initializes a new <code>PrintStream</code> object to write + * to the specified output File. Doesn't autoflush. + * + * @param fileName The name of the <code>File</code> to write to. + * @param encoding The name of the character encoding to use for this + * object. + * @throws FileNotFoundException if an error occurs while opening the file. + * @throws UnsupportedEncodingException If the charset specified by + * <code>encoding</code> is invalid. + * + * @since 1.5 + */ + public PrintStream (String fileName, String encoding) + throws FileNotFoundException,UnsupportedEncodingException + { + this (new FileOutputStream(new File(fileName)), false, encoding); + } + + /** + * This method initializes a new <code>PrintStream</code> object to write + * to the specified output sink. Doesn't autoflush. * * @param out The <code>OutputStream</code> to write to. */ @@ -98,7 +166,7 @@ public class PrintStream extends FilterOutputStream } /** - * This method intializes a new <code>PrintStream</code> object to write + * This method initializes a new <code>PrintStream</code> object to write * to the specified output sink. This constructor also allows "auto-flush" * functionality to be specified where the stream will be flushed after * every <code>print</code> or <code>println</code> call, when the @@ -127,7 +195,7 @@ public class PrintStream extends FilterOutputStream } /** - * This method intializes a new <code>PrintStream</code> object to write + * This method initializes a new <code>PrintStream</code> object to write * to the specified output sink. This constructor also allows "auto-flush" * functionality to be specified where the stream will be flushed after * every <code>print</code> or <code>println</code> call, when the diff --git a/libjava/classpath/java/lang/Iterable.java b/libjava/classpath/java/lang/Iterable.java index 8223bcf8c93..35c426484bb 100644 --- a/libjava/classpath/java/lang/Iterable.java +++ b/libjava/classpath/java/lang/Iterable.java @@ -38,7 +38,8 @@ exception statement from your version. */ package java.lang; -import java.util.Iterator; +// We only need Iterator, but we import * to support lib/mkcollections.pl +import java.util.*; /** * This interface is used to indicate that a given class can be diff --git a/libjava/classpath/java/lang/SecurityManager.java b/libjava/classpath/java/lang/SecurityManager.java index 30ee1be086f..999fe83dda1 100644 --- a/libjava/classpath/java/lang/SecurityManager.java +++ b/libjava/classpath/java/lang/SecurityManager.java @@ -421,7 +421,7 @@ public class SecurityManager public void checkAccess(Thread thread) { if (thread.getThreadGroup() != null - && thread.getThreadGroup().getParent() == null) + && thread.getThreadGroup().parent == null) checkPermission(new RuntimePermission("modifyThread")); } @@ -454,7 +454,7 @@ public class SecurityManager */ public void checkAccess(ThreadGroup g) { - if (g.getParent() == null) + if (g.parent == null) checkPermission(new RuntimePermission("modifyThreadGroup")); } diff --git a/libjava/classpath/java/lang/StrictMath.java b/libjava/classpath/java/lang/StrictMath.java index 548a6f1c16e..0f066216787 100644 --- a/libjava/classpath/java/lang/StrictMath.java +++ b/libjava/classpath/java/lang/StrictMath.java @@ -1,5 +1,5 @@ /* java.lang.StrictMath -- common mathematical functions, strict Java - Copyright (C) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1998, 2001, 2002, 2003, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -633,6 +633,227 @@ public final strictfp class StrictMath } /** + * Returns the hyperbolic cosine of <code>x</code>, which is defined as + * (exp(x) + exp(-x)) / 2. + * + * Special cases: + * <ul> + * <li>If the argument is NaN, the result is NaN</li> + * <li>If the argument is positive infinity, the result is positive + * infinity.</li> + * <li>If the argument is negative infinity, the result is positive + * infinity.</li> + * <li>If the argument is zero, the result is one.</li> + * </ul> + * + * @param x the argument to <em>cosh</em> + * @return the hyperbolic cosine of <code>x</code> + * + * @since 1.5 + */ + public static double cosh(double x) + { + // Method : + // mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + // 1. Replace x by |x| (cosh(x) = cosh(-x)). + // 2. + // [ exp(x) - 1 ]^2 + // 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + // 2*exp(x) + // + // exp(x) + 1/exp(x) + // ln2/2 <= x <= 22 : cosh(x) := ------------------ + // 2 + // 22 <= x <= lnovft : cosh(x) := exp(x)/2 + // lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + // ln2ovft < x : cosh(x) := +inf (overflow) + + double t, w; + long bits; + int hx; + int lx; + + // handle special cases + if (x != x) + return Double.NaN; + if (x == Double.POSITIVE_INFINITY) + return Double.POSITIVE_INFINITY; + if (x == Double.NEGATIVE_INFINITY) + return Double.POSITIVE_INFINITY; + + bits = Double.doubleToLongBits(x); + hx = getHighDWord(bits) & 0x7fffffff; // ignore sign + lx = getLowDWord(bits); + + // |x| in [0, 0.5 * ln(2)], return 1 + expm1(|x|)^2 / (2 * exp(|x|)) + if (hx < 0x3fd62e43) + { + t = expm1(abs(x)); + w = 1.0 + t; + + // for tiny arguments return 1. + if (hx < 0x3c800000) + return w; + + return 1.0 + (t * t) / (w + w); + } + + // |x| in [0.5 * ln(2), 22], return exp(|x|)/2 + 1 / (2 * exp(|x|)) + if (hx < 0x40360000) + { + t = exp(abs(x)); + + return 0.5 * t + 0.5 / t; + } + + // |x| in [22, log(Double.MAX_VALUE)], return 0.5 * exp(|x|) + if (hx < 0x40862e42) + return 0.5 * exp(abs(x)); + + // |x| in [log(Double.MAX_VALUE), overflowthreshold], + // return exp(x/2)/2 * exp(x/2) + + // we need to force an unsigned <= compare, thus can not use lx. + if ((hx < 0x408633ce) + || ((hx == 0x408633ce) + && ((bits & 0x00000000ffffffffL) <= 0x8fb9f87dL))) + { + w = exp(0.5 * abs(x)); + t = 0.5 * w; + + return t * w; + } + + // |x| > overflowthreshold + return Double.POSITIVE_INFINITY; + } + + /** + * Returns the lower two words of a long. This is intended to be + * used like this: + * <code>getLowDWord(Double.doubleToLongBits(x))</code>. + */ + private static int getLowDWord(long x) + { + return (int) (x & 0x00000000ffffffffL); + } + + /** + * Returns the higher two words of a long. This is intended to be + * used like this: + * <code>getHighDWord(Double.doubleToLongBits(x))</code>. + */ + private static int getHighDWord(long x) + { + return (int) ((x & 0xffffffff00000000L) >> 32); + } + + /** + * Returns a double with the IEEE754 bit pattern given in the lower + * and higher two words <code>lowDWord</code> and <code>highDWord</code>. + */ + private static double buildDouble(int lowDWord, int highDWord) + { + return Double.longBitsToDouble((((long) highDWord & 0xffffffffL) << 32) + | ((long) lowDWord & 0xffffffffL)); + } + + /** + * Returns the cube root of <code>x</code>. The sign of the cube root + * is equal to the sign of <code>x</code>. + * + * Special cases: + * <ul> + * <li>If the argument is NaN, the result is NaN</li> + * <li>If the argument is positive infinity, the result is positive + * infinity.</li> + * <li>If the argument is negative infinity, the result is negative + * infinity.</li> + * <li>If the argument is zero, the result is zero with the same + * sign as the argument.</li> + * </ul> + * + * @param x the number to take the cube root of + * @return the cube root of <code>x</code> + * @see #sqrt(double) + * + * @since 1.5 + */ + public static double cbrt(double x) + { + boolean negative = (x < 0); + double r; + double s; + double t; + double w; + + long bits; + int l; + int h; + + // handle the special cases + if (x != x) + return Double.NaN; + if (x == Double.POSITIVE_INFINITY) + return Double.POSITIVE_INFINITY; + if (x == Double.NEGATIVE_INFINITY) + return Double.NEGATIVE_INFINITY; + if (x == 0) + return x; + + x = abs(x); + bits = Double.doubleToLongBits(x); + + if (bits < 0x0010000000000000L) // subnormal number + { + t = TWO_54; + t *= x; + + // __HI(t)=__HI(t)/3+B2; + bits = Double.doubleToLongBits(t); + h = getHighDWord(bits); + l = getLowDWord(bits); + + h = h / 3 + CBRT_B2; + + t = buildDouble(l, h); + } + else + { + // __HI(t)=__HI(x)/3+B1; + h = getHighDWord(bits); + l = 0; + + h = h / 3 + CBRT_B1; + t = buildDouble(l, h); + } + + // new cbrt to 23 bits + r = t * t / x; + s = CBRT_C + r * t; + t *= CBRT_G + CBRT_F / (s + CBRT_E + CBRT_D / s); + + // chopped to 20 bits and make it larger than cbrt(x) + bits = Double.doubleToLongBits(t); + h = getHighDWord(bits); + + // __LO(t)=0; + // __HI(t)+=0x00000001; + l = 0; + h += 1; + t = buildDouble(l, h); + + // one step newton iteration to 53 bits with error less than 0.667 ulps + s = t * t; // t * t is exact + r = x / s; + w = t + t; + r = (r - t) / (w + r); // r - s is exact + t = t + t * r; + + return negative ? -t : t; + } + + /** * Take <em>e</em><sup>a</sup>. The opposite of <code>log()</code>. If the * argument is NaN, the result is NaN; if the argument is positive infinity, * the result is positive infinity; and if the argument is negative @@ -694,6 +915,254 @@ public final strictfp class StrictMath } /** + * Returns <em>e</em><sup>x</sup> - 1. + * Special cases: + * <ul> + * <li>If the argument is NaN, the result is NaN.</li> + * <li>If the argument is positive infinity, the result is positive + * infinity</li> + * <li>If the argument is negative infinity, the result is -1.</li> + * <li>If the argument is zero, the result is zero.</li> + * </ul> + * + * @param x the argument to <em>e</em><sup>x</sup> - 1. + * @return <em>e</em> raised to the power <code>x</code> minus one. + * @see #exp(double) + */ + public static double expm1(double x) + { + // Method + // 1. Argument reduction: + // Given x, find r and integer k such that + // + // x = k * ln(2) + r, |r| <= 0.5 * ln(2) + // + // Here a correction term c will be computed to compensate + // the error in r when rounded to a floating-point number. + // + // 2. Approximating expm1(r) by a special rational function on + // the interval [0, 0.5 * ln(2)]: + // Since + // r*(exp(r)+1)/(exp(r)-1) = 2 + r^2/6 - r^4/360 + ... + // we define R1(r*r) by + // r*(exp(r)+1)/(exp(r)-1) = 2 + r^2/6 * R1(r*r) + // That is, + // R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) + // = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) + // = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... + // We use a special Remes algorithm on [0, 0.347] to generate + // a polynomial of degree 5 in r*r to approximate R1. The + // maximum error of this polynomial approximation is bounded + // by 2**-61. In other words, + // R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 + // where Q1 = -1.6666666666666567384E-2, + // Q2 = 3.9682539681370365873E-4, + // Q3 = -9.9206344733435987357E-6, + // Q4 = 2.5051361420808517002E-7, + // Q5 = -6.2843505682382617102E-9; + // (where z=r*r, and Q1 to Q5 are called EXPM1_Qx in the source) + // with error bounded by + // | 5 | -61 + // | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 + // | | + // + // expm1(r) = exp(r)-1 is then computed by the following + // specific way which minimize the accumulation rounding error: + // 2 3 + // r r [ 3 - (R1 + R1*r/2) ] + // expm1(r) = r + --- + --- * [--------------------] + // 2 2 [ 6 - r*(3 - R1*r/2) ] + // + // To compensate the error in the argument reduction, we use + // expm1(r+c) = expm1(r) + c + expm1(r)*c + // ~ expm1(r) + c + r*c + // Thus c+r*c will be added in as the correction terms for + // expm1(r+c). Now rearrange the term to avoid optimization + // screw up: + // ( 2 2 ) + // ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) + // expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) + // ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) + // ( ) + // + // = r - E + // 3. Scale back to obtain expm1(x): + // From step 1, we have + // expm1(x) = either 2^k*[expm1(r)+1] - 1 + // = or 2^k*[expm1(r) + (1-2^-k)] + // 4. Implementation notes: + // (A). To save one multiplication, we scale the coefficient Qi + // to Qi*2^i, and replace z by (x^2)/2. + // (B). To achieve maximum accuracy, we compute expm1(x) by + // (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) + // (ii) if k=0, return r-E + // (iii) if k=-1, return 0.5*(r-E)-0.5 + // (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) + // else return 1.0+2.0*(r-E); + // (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) + // (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else + // (vii) return 2^k(1-((E+2^-k)-r)) + + boolean negative = (x < 0); + double y, hi, lo, c, t, e, hxs, hfx, r1; + int k; + + long bits; + int h_bits; + int l_bits; + + c = 0.0; + y = abs(x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + // handle special cases and large arguments + if (h_bits >= 0x4043687a) // if |x| >= 56 * ln(2) + { + if (h_bits >= 0x40862e42) // if |x| >= EXP_LIMIT_H + { + if (h_bits >= 0x7ff00000) + { + if (((h_bits & 0x000fffff) | (l_bits & 0xffffffff)) != 0) + return Double.NaN; // exp(NaN) = NaN + else + return negative ? -1.0 : x; // exp({+-inf}) = {+inf, -1} + } + + if (x > EXP_LIMIT_H) + return Double.POSITIVE_INFINITY; // overflow + } + + if (negative) // x <= -56 * ln(2) + return -1.0; + } + + // argument reduction + if (h_bits > 0x3fd62e42) // |x| > 0.5 * ln(2) + { + if (h_bits < 0x3ff0a2b2) // |x| < 1.5 * ln(2) + { + if (negative) + { + hi = x + LN2_H; + lo = -LN2_L; + k = -1; + } + else + { + hi = x - LN2_H; + lo = LN2_L; + k = 1; + } + } + else + { + k = (int) (INV_LN2 * x + (negative ? - 0.5 : 0.5)); + t = k; + hi = x - t * LN2_H; + lo = t * LN2_L; + } + + x = hi - lo; + c = (hi - x) - lo; + + } + else if (h_bits < 0x3c900000) // |x| < 2^-54 return x + return x; + else + k = 0; + + // x is now in primary range + hfx = 0.5 * x; + hxs = x * hfx; + r1 = 1.0 + hxs * (EXPM1_Q1 + + hxs * (EXPM1_Q2 + + hxs * (EXPM1_Q3 + + hxs * (EXPM1_Q4 + + hxs * EXPM1_Q5)))); + t = 3.0 - r1 * hfx; + e = hxs * ((r1 - t) / (6.0 - x * t)); + + if (k == 0) + { + return x - (x * e - hxs); // c == 0 + } + else + { + e = x * (e - c) - c; + e -= hxs; + + if (k == -1) + return 0.5 * (x - e) - 0.5; + + if (k == 1) + { + if (x < - 0.25) + return -2.0 * (e - (x + 0.5)); + else + return 1.0 + 2.0 * (x - e); + } + + if (k <= -2 || k > 56) // sufficient to return exp(x) - 1 + { + y = 1.0 - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + + return y - 1.0; + } + + t = 1.0; + if (k < 20) + { + bits = Double.doubleToLongBits(t); + h_bits = 0x3ff00000 - (0x00200000 >> k); + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 1 - 2^(-k) + y = t - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + else + { + bits = Double.doubleToLongBits(t); + h_bits = (0x000003ff - k) << 20; + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 2^(-k) + + y = x - (e + t); + y += 1.0; + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + } + + return y; + } + + /** * Take ln(a) (the natural log). The opposite of <code>exp()</code>. If the * argument is NaN or negative, the result is NaN; if the argument is * positive infinity, the result is positive infinity; and if the argument @@ -1429,6 +1898,33 @@ public final strictfp class StrictMath AT10 = 0.016285820115365782; // Long bits 0x3f90ad3ae322da11L. /** + * Constants for computing {@link #cbrt(double)}. + */ + private static final int + CBRT_B1 = 715094163, // B1 = (682-0.03306235651)*2**20 + CBRT_B2 = 696219795; // B2 = (664-0.03306235651)*2**20 + + /** + * Constants for computing {@link #cbrt(double)}. + */ + private static final double + CBRT_C = 5.42857142857142815906e-01, // Long bits 0x3fe15f15f15f15f1L + CBRT_D = -7.05306122448979611050e-01, // Long bits 0xbfe691de2532c834L + CBRT_E = 1.41428571428571436819e+00, // Long bits 0x3ff6a0ea0ea0ea0fL + CBRT_F = 1.60714285714285720630e+00, // Long bits 0x3ff9b6db6db6db6eL + CBRT_G = 3.57142857142857150787e-01; // Long bits 0x3fd6db6db6db6db7L + + /** + * Constants for computing {@link #expm1(double)} + */ + private static final double + EXPM1_Q1 = -3.33333333333331316428e-02, // Long bits 0xbfa11111111110f4L + EXPM1_Q2 = 1.58730158725481460165e-03, // Long bits 0x3f5a01a019fe5585L + EXPM1_Q3 = -7.93650757867487942473e-05, // Long bits 0xbf14ce199eaadbb7L + EXPM1_Q4 = 4.00821782732936239552e-06, // Long bits 0x3ed0cfca86e65239L + EXPM1_Q5 = -2.01099218183624371326e-07; // Long bits 0xbe8afdb76e09c32dL + + /** * Helper function for reducing an angle to a multiple of pi/2 within * [-pi/4, pi/4]. * diff --git a/libjava/classpath/java/lang/String.java b/libjava/classpath/java/lang/String.java index 199dfba3545..dbc3f7d8bf4 100644 --- a/libjava/classpath/java/lang/String.java +++ b/libjava/classpath/java/lang/String.java @@ -1820,7 +1820,7 @@ public final class String implements Serializable, Comparable, CharSequence */ public synchronized int codePointCount(int start, int end) { - if (start < 0 || end >= count || start > end) + if (start < 0 || end > count || start > end) throw new StringIndexOutOfBoundsException(); start += offset; diff --git a/libjava/classpath/java/lang/System.java b/libjava/classpath/java/lang/System.java index b538b795b95..19278aa32cd 100644 --- a/libjava/classpath/java/lang/System.java +++ b/libjava/classpath/java/lang/System.java @@ -222,6 +222,36 @@ public final class System return VMSystem.currentTimeMillis(); } + /** + * <p> + * Returns the current value of a nanosecond-precise system timer. + * The value of the timer is an offset relative to some arbitrary fixed + * time, which may be in the future (making the value negative). This + * method is useful for timing events where nanosecond precision is + * required. This is achieved by calling this method before and after the + * event, and taking the difference betweent the two times: + * </p> + * <p> + * <code>long startTime = System.nanoTime();</code><br /> + * <code>... <emph>event code</emph> ...</code><br /> + * <code>long endTime = System.nanoTime();</code><br /> + * <code>long duration = endTime - startTime;</code><br /> + * </p> + * <p> + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. + * </p> + * + * @return the time of a system timer in nanoseconds. + * @since 1.5 + */ + public static long nanoTime() + { + return VMSystem.nanoTime(); + } + /** * Copy one array onto another from <code>src[srcStart]</code> ... * <code>src[srcStart+len-1]</code> to <code>dest[destStart]</code> ... @@ -319,6 +349,7 @@ public final class System * <dt>gnu.java.io.encoding_scheme_alias.iso-latin-_?</dt> <dd>8859_?</dd> * <dt>gnu.java.io.encoding_scheme_alias.latin?</dt> <dd>8859_?</dd> * <dt>gnu.java.io.encoding_scheme_alias.utf-8</dt> <dd>UTF8</dd> + * <dt>gnu.javax.print.server</dt> <dd>Hostname of external CUPS server.</dd> * </dl> * * @return the system properties, will never be null diff --git a/libjava/classpath/java/lang/Thread.java b/libjava/classpath/java/lang/Thread.java index 23620543aef..36b7c3303a9 100644 --- a/libjava/classpath/java/lang/Thread.java +++ b/libjava/classpath/java/lang/Thread.java @@ -38,8 +38,16 @@ exception statement from your version. */ package java.lang; +import gnu.classpath.VMStackWalker; import gnu.java.util.WeakIdentityHashMap; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + import java.security.Permission; + +import java.util.HashMap; import java.util.Map; /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 @@ -131,15 +139,16 @@ public class Thread implements Runnable /** The context classloader for this Thread. */ private ClassLoader contextClassLoader; - + private boolean contextClassLoaderIsSystemClassLoader; + /** This thread's ID. */ private final long threadId; /** The next thread number to use. */ private static int numAnonymousThreadsCreated; - /** The next thread ID to use. */ - private static long nextThreadId; + /** Used to generate the next thread ID to use. */ + private static long totalThreadsCreated; /** The default exception handler. */ private static UncaughtExceptionHandler defaultHandler; @@ -248,7 +257,7 @@ public class Thread implements Runnable */ public Thread(ThreadGroup group, Runnable target) { - this(group, target, "Thread-" + ++numAnonymousThreadsCreated, 0); + this(group, target, createAnonymousThreadName(), 0); } /** @@ -347,8 +356,8 @@ public class Thread implements Runnable if (group == null) group = current.group; } - else if (sm != null) - sm.checkAccess(group); + if (sm != null) + sm.checkAccess(group); this.group = group; // Use toString hack to detect null. @@ -358,12 +367,14 @@ public class Thread implements Runnable synchronized (Thread.class) { - this.threadId = nextThreadId++; + this.threadId = ++totalThreadsCreated; } priority = current.priority; daemon = current.daemon; contextClassLoader = current.contextClassLoader; + contextClassLoaderIsSystemClassLoader = + current.contextClassLoaderIsSystemClassLoader; group.addThread(this); InheritableThreadLocal.newChildThread(this); @@ -373,6 +384,9 @@ public class Thread implements Runnable * Used by the VM to create thread objects for threads started outside * of Java. Note: caller is responsible for adding the thread to * a group and InheritableThreadLocal. + * Note: This constructor should not call any methods that could result + * in a call to Thread.currentThread(), because that makes life harder + * for the VM. * * @param vmThread the native thread * @param name the thread name or null to use the default naming scheme @@ -384,16 +398,32 @@ public class Thread implements Runnable this.vmThread = vmThread; this.runnable = null; if (name == null) - name = "Thread-" + ++numAnonymousThreadsCreated; + name = createAnonymousThreadName(); this.name = name; this.priority = priority; this.daemon = daemon; - this.contextClassLoader = ClassLoader.getSystemClassLoader(); + // By default the context class loader is the system class loader, + // we set a flag to signal this because we don't want to call + // ClassLoader.getSystemClassLoader() at this point, because on + // VMs that lazily create the system class loader that might result + // in running user code (when a custom system class loader is specified) + // and that user code could call Thread.currentThread(). + // ClassLoader.getSystemClassLoader() can also return null, if the system + // is currently in the process of constructing the system class loader + // (and, as above, the constructiong sequence calls Thread.currenThread()). + contextClassLoaderIsSystemClassLoader = true; synchronized (Thread.class) { - this.threadId = nextThreadId++; + this.threadId = ++totalThreadsCreated; } + } + /** + * Generate a name for an anonymous thread. + */ + private static synchronized String createAnonymousThreadName() + { + return "Thread-" + ++numAnonymousThreadsCreated; } /** @@ -746,12 +776,18 @@ public class Thread implements Runnable */ public synchronized ClassLoader getContextClassLoader() { - // Bypass System.getSecurityManager, for bootstrap efficiency. + ClassLoader loader = contextClassLoaderIsSystemClassLoader ? + ClassLoader.getSystemClassLoader() : contextClassLoader; + // Check if we may get the classloader SecurityManager sm = SecurityManager.current; - if (sm != null) - // XXX Don't check this if the caller's class loader is an ancestor. - sm.checkPermission(new RuntimePermission("getClassLoader")); - return contextClassLoader; + if (loader != null && sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && !cl.isAncestorOf(loader)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return loader; } /** @@ -772,6 +808,7 @@ public class Thread implements Runnable if (sm != null) sm.checkPermission(new RuntimePermission("setContextClassLoader")); this.contextClassLoader = classloader; + contextClassLoaderIsSystemClassLoader = false; } /** @@ -1173,7 +1210,7 @@ public class Thread implements Runnable * @author Andrew John Hughes <gnu_andrew@member.fsf.org> * @since 1.5 * @see Thread#getUncaughtExceptionHandler() - * @see Thread#setUncaughtExceptionHander(java.lang.Thread.UncaughtExceptionHandler) + * @see Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler) * @see Thread#getDefaultUncaughtExceptionHandler() * @see * Thread#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler) @@ -1191,4 +1228,119 @@ public class Thread implements Runnable */ void uncaughtException(Thread thr, Throwable exc); } + + /** + * Returns the current state of the thread. This + * is designed for monitoring thread behaviour, rather + * than for synchronization control. + * + * @return the current thread state. + */ + public String getState() + { + VMThread t = vmThread; + if (t != null) + return t.getState(); + if (group == null) + return "TERMINATED"; + return "NEW"; + } + + /** + * <p> + * Returns a map of threads to stack traces for each + * live thread. The keys of the map are {@link Thread} + * objects, which map to arrays of {@link StackTraceElement}s. + * The results obtained from Calling this method are + * equivalent to calling {@link getStackTrace()} on each + * thread in succession. Threads may be executing while + * this takes place, and the results represent a snapshot + * of the thread at the time its {@link getStackTrace()} + * method is called. + * </p> + * <p> + * The stack trace information contains the methods called + * by the thread, with the most recent method forming the + * first element in the array. The array will be empty + * if the virtual machine can not obtain information on the + * thread. + * </p> + * <p> + * To execute this method, the current security manager + * (if one exists) must allow both the + * <code>"getStackTrace"</code> and + * <code>"modifyThreadGroup"</code> {@link RuntimePermission}s. + * </p> + * + * @return a map of threads to arrays of {@link StackTraceElement}s. + * @throws SecurityException if a security manager exists, and + * prevents either or both the runtime + * permissions specified above. + * @since 1.5 + * @see #getStackTrace() + */ + public static Map getAllStackTraces() + { + ThreadGroup group = currentThread().group; + while (group.getParent() != null) + group = group.getParent(); + int arraySize = group.activeCount(); + Thread[] threadList = new Thread[arraySize]; + int filled = group.enumerate(threadList); + while (filled == arraySize) + { + arraySize *= 2; + threadList = new Thread[arraySize]; + filled = group.enumerate(threadList); + } + Map traces = new HashMap(); + for (int a = 0; a < filled; ++a) + traces.put(threadList[a], + threadList[a].getStackTrace()); + return traces; + } + + /** + * <p> + * Returns an array of {@link StackTraceElement}s + * representing the current stack trace of this thread. + * The first element of the array is the most recent + * method called, and represents the top of the stack. + * The elements continue in this order, with the last + * element representing the bottom of the stack. + * </p> + * <p> + * A zero element array is returned for threads which + * have not yet started (and thus have not yet executed + * any methods) or for those which have terminated. + * Where the virtual machine can not obtain a trace for + * the thread, an empty array is also returned. The + * virtual machine may also omit some methods from the + * trace in non-zero arrays. + * </p> + * <p> + * To execute this method, the current security manager + * (if one exists) must allow both the + * <code>"getStackTrace"</code> and + * <code>"modifyThreadGroup"</code> {@link RuntimePermission}s. + * </p> + * + * @return a stack trace for this thread. + * @throws SecurityException if a security manager exists, and + * prevents the use of the + * <code>"getStackTrace"</code> + * permission. + * @since 1.5 + * @see #getAllStackTraces() + */ + public StackTraceElement[] getStackTrace() + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("getStackTrace")); + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + ThreadInfo info = bean.getThreadInfo(threadId, Integer.MAX_VALUE); + return info.getStackTrace(); + } + } diff --git a/libjava/classpath/java/lang/ThreadGroup.java b/libjava/classpath/java/lang/ThreadGroup.java index 7fbef88f4d6..00f2f8ec47c 100644 --- a/libjava/classpath/java/lang/ThreadGroup.java +++ b/libjava/classpath/java/lang/ThreadGroup.java @@ -66,7 +66,7 @@ public class ThreadGroup implements UncaughtExceptionHandler static boolean had_uncaught_exception; /** The parent thread group. */ - private final ThreadGroup parent; + final ThreadGroup parent; /** The group name, non-null. */ final String name; @@ -749,4 +749,43 @@ public class ThreadGroup implements UncaughtExceptionHandler parent.removeGroup(this); } } + + /* + * Helper method for the VM. Find a Thread by its Id. + * + * @param id The Thread Id. + * @return Thread object or null if thread doesn't exist. + */ + static Thread getThreadFromId(long id) + { + return root.getThreadFromIdImpl(id); + } + + private Thread getThreadFromIdImpl(long id) + { + synchronized (threads) + { + for (int i = 0; i < threads.size(); i++) + { + Thread t = (Thread) threads.get(i); + if (t.getId() == id) + return t; + } + } + Vector groups = this.groups; + if (groups != null) + { + synchronized (groups) + { + for (int i = 0; i < groups.size(); i++) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + Thread t = g.getThreadFromIdImpl(id); + if (t != null) + return t; + } + } + } + return null; + } } // class ThreadGroup diff --git a/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java b/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java new file mode 100644 index 00000000000..a35df0c09b8 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java @@ -0,0 +1,106 @@ +/* IncompleteAnnotationException.java - Thrown when annotation has changed + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * Thrown when accessing an element within an annotation which + * was added since compilation or serialization took place, and + * does not have a default value. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IncompleteAnnotationException extends RuntimeException +{ + + /** + * Constructs a new <code>IncompleteAnnotationException</code> + * which indicates that the element, <code>name</code>, was missing + * from the annotation, <code>type</code> at compile time and does + * not have a default value. + * + * @param type the type of annotation from which an element is missing. + * @param name the name of the missing element. + */ + public IncompleteAnnotationException(Class type, String name) + { + this.annotationType = type; + this.elementName = name; + } + + /** + * Returns the class representing the type of annotation + * from which an element was missing. + * + * @return the type of annotation. + */ + public Class annotationType() + { + return annotationType; + } + + /** + * Returns the name of the missing annotation element. + * + * @return the element name. + */ + public String elementName() + { + return elementName; + } + + // Names are chosen from serialization spec. + + /** + * The class representing the type of annotation from + * which an element was found to be missing. + * + * @serial the type of the annotation from which an + * element was missing. + */ + private Class annotationType; + + /** + * The name of the missing element. + * + * @serial the name of the missing element. + */ + private String elementName; + +} diff --git a/libjava/classpath/java/lang/management/ClassLoadingMXBean.java b/libjava/classpath/java/lang/management/ClassLoadingMXBean.java new file mode 100644 index 00000000000..2a8651b9c81 --- /dev/null +++ b/libjava/classpath/java/lang/management/ClassLoadingMXBean.java @@ -0,0 +1,103 @@ +/* ClassLoadingMXBean.java - Interface for a class loading bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the class loading + * behaviour of the current invocation of the virtual + * machine. An instance of this bean is obtained by calling + * {@link ManagementFactory#getClassLoadingMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface ClassLoadingMXBean +{ + + /** + * Returns the number of classes currently loaded by + * the virtual machine. + * + * @return the number of loaded classes. + */ + int getLoadedClassCount(); + + /** + * Returns the total number of classes loaded by the + * virtual machine since it was started. This is the + * sum of the currently loaded classes and those that + * have been unloaded. + * + * @return the total number of classes that have been + * loaded by the virtual machine since it started. + */ + long getTotalLoadedClassCount(); + + /** + * Returns the number of classes that have been unloaded + * by the virtual machine since it was started. + * + * @return the number of unloaded classes. + */ + long getUnloadedClassCount(); + + /** + * Returns true if the virtual machine will emit additional + * information when classes are loaded and unloaded. The + * format of the output is left up to the virtual machine. + * + * @return true if verbose class loading output is on. + */ + boolean isVerbose(); + + /** + * Turns on or off the emission of additional information + * when classes are loaded and unloaded. The format of the + * output is left up to the virtual machine. This method + * may be called by multiple threads concurrently, but there + * is only one global setting of verbosity that is affected. + * + * @param verbose the new setting for verbose class loading + * output. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void setVerbose(boolean verbose); + +} + diff --git a/libjava/classpath/java/lang/management/CompilationMXBean.java b/libjava/classpath/java/lang/management/CompilationMXBean.java new file mode 100644 index 00000000000..7f666f2c64b --- /dev/null +++ b/libjava/classpath/java/lang/management/CompilationMXBean.java @@ -0,0 +1,85 @@ +/* CompilationMXBean.java - Interface for a compilation bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the Just-In-Time + * (JIT) compiler provided by the virtual machine, if one + * exists. An instance of this bean is obtainable by + * calling {@link ManagementFactory#getCompilationMXBean()} + * if a JIT is available. Otherwise, the method returns + * <code>null</code>. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface CompilationMXBean +{ + + /** + * Returns the name of the Just-In-Time (JIT) compiler. + * + * @return the name of the JIT compiler. + */ + String getName(); + + /** + * Returns true if the virtual machine's JIT compiler + * supports monitoring of the time spent compiling. + * + * @return true if the JIT compiler can be monitored + * for time spent compiling. + */ + boolean isCompilationTimeMonitoringSupported(); + + /** + * Returns the accumulated time, in milliseconds, that + * the JIT compiler has spent compiling Java bytecodes + * to native machine code. This value represents a single + * time measurement for the whole virtual machine, including + * all multiple threads of operation. The value is not + * intended as a performance measurement. + * + * @return the accumulated number of milliseconds the JIT + * compiler has spent compiling. + * @throws UnsupportedOperationException if time monitoring + * is not supported. + */ + long getTotalCompilationTime(); + +} diff --git a/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java b/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java new file mode 100644 index 00000000000..5bb0a18d386 --- /dev/null +++ b/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java @@ -0,0 +1,79 @@ +/* GarbageCollectorMXBean.java - Interface for a garbage collector bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the garbage collectors + * of the virtual machine. Garbage collectors are responsible + * for removing unreferenced objects from memory. A garbage + * collector is a type of memory manager, so this interface + * is combined with that of generic memory managers. An instance + * of this bean for each garbage collector is obtained by calling + * {@link ManagementFactory#getGarbageCollectorMXBeans()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface GarbageCollectorMXBean + extends MemoryManagerMXBean +{ + + /** + * Returns the number of collections the garbage collector + * represented by this bean has made. -1 is returned if the + * collection count is undefined. + * + * @return the number of collections made, or -1 if this is + * undefined. + */ + long getCollectionCount(); + + /** + * Returns the accumulated number of milliseconds this garbage + * collector has spent freeing the memory used by unreferenced + * objects. -1 is returned if the collection time is undefined. + * Note that the accumulated time may not change, even when the + * collection count increases, if the time taken is sufficiently + * short; this depends on the resolution of the timer used. + * + * @return the accumulated number of milliseconds spent collecting, + * or -1 if this is undefined. + */ + long getCollectionTime(); + +} diff --git a/libjava/classpath/java/lang/management/ManagementFactory.java b/libjava/classpath/java/lang/management/ManagementFactory.java new file mode 100644 index 00000000000..6e7af0f274e --- /dev/null +++ b/libjava/classpath/java/lang/management/ManagementFactory.java @@ -0,0 +1,331 @@ +/* ManagementFactory.java - Factory for obtaining system beans. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.management.ClassLoadingMXBeanImpl; +import gnu.java.lang.management.CompilationMXBeanImpl; +import gnu.java.lang.management.GarbageCollectorMXBeanImpl; +import gnu.java.lang.management.OperatingSystemMXBeanImpl; +import gnu.java.lang.management.MemoryMXBeanImpl; +import gnu.java.lang.management.MemoryManagerMXBeanImpl; +import gnu.java.lang.management.MemoryPoolMXBeanImpl; +import gnu.java.lang.management.RuntimeMXBeanImpl; +import gnu.java.lang.management.ThreadMXBeanImpl; + +import java.util.ArrayList; +import java.util.List; + +import javax.management.NotCompliantMBeanException; + +/** + * <p> + * Provides access to the system's management beans via a series + * of static methods. + * </p> + * <p> + * An instance of a system management bean can be obtained by + * using one of the following methods: + * </p> + * <ol> + * <li>Calling the appropriate static method of this factory. + * </li> + * </ol> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class ManagementFactory +{ + + /** + * The operating system management bean. + */ + private static OperatingSystemMXBean osBean; + + /** + * The runtime management bean. + */ + private static RuntimeMXBean runtimeBean; + + /** + * The class loading management bean. + */ + private static ClassLoadingMXBean classLoadingBean; + + /** + * The thread bean. + */ + private static ThreadMXBean threadBean; + + /** + * The memory bean. + */ + private static MemoryMXBean memoryBean; + + /** + * The compilation bean (may remain null). + */ + private static CompilationMXBean compilationBean; + + /** + * Private constructor to prevent instance creation. + */ + private ManagementFactory() {} + + /** + * Returns the operating system management bean for the + * operating system on which the virtual machine is running. + * + * @return an instance of {@link OperatingSystemMXBean} for + * the underlying operating system. + */ + public static OperatingSystemMXBean getOperatingSystemMXBean() + { + if (osBean == null) + try + { + osBean = new OperatingSystemMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "operating system bean is not a " + + "compliant management bean."); + } + return osBean; + } + + /** + * Returns the runtime management bean for the + * running virtual machine. + * + * @return an instance of {@link RuntimeMXBean} for + * this virtual machine. + */ + public static RuntimeMXBean getRuntimeMXBean() + { + if (runtimeBean == null) + try + { + runtimeBean = new RuntimeMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "runtime bean is not a compliant " + + "management bean."); + } + return runtimeBean; + } + + /** + * Returns the class loading management bean for the + * running virtual machine. + * + * @return an instance of {@link ClassLoadingMXBean} for + * this virtual machine. + */ + public static ClassLoadingMXBean getClassLoadingMXBean() + { + if (classLoadingBean == null) + try + { + classLoadingBean = new ClassLoadingMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "class loading bean is not a " + + "compliant management bean."); + } + return classLoadingBean; + } + + /** + * Returns the thread management bean for the running + * virtual machine. + * + * @return an instance of {@link ThreadMXBean} for + * this virtual machine. + */ + public static ThreadMXBean getThreadMXBean() + { + if (threadBean == null) + try + { + threadBean = new ThreadMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "thread bean is not a compliant " + + "management bean."); + } + return threadBean; + } + + /** + * Returns the memory management bean for the running + * virtual machine. + * + * @return an instance of {@link MemoryMXBean} for + * this virtual machine. + */ + public static MemoryMXBean getMemoryMXBean() + { + if (memoryBean == null) + try + { + memoryBean = new MemoryMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory bean is not a compliant " + + "management bean."); + } + return memoryBean; + } + + /** + * Returns the compilation bean for the running + * virtual machine, if supported. Otherwise, + * it returns <code>null</code>. + * + * @return an instance of {@link CompilationMXBean} for + * this virtual machine, or <code>null</code> + * if the virtual machine doesn't include + * a Just-In-Time (JIT) compiler. + */ + public static CompilationMXBean getCompilationMXBean() + { + if (compilationBean == null && + SystemProperties.getProperty("gnu.java.compiler.name") != null) + try + { + compilationBean = new CompilationMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "compilation bean is not a compliant " + + "management bean."); + } + return compilationBean; + } + + /** + * Returns the memory pool beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of memory pool beans, one for each pool. + */ + public static List getMemoryPoolMXBeans() + { + List poolBeans = new ArrayList(); + String[] names = VMManagementFactory.getMemoryPoolNames(); + for (int a = 0; a < names.length; ++a) + try + { + poolBeans.add(new MemoryPoolMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory pool bean, " + a + ", is " + + "not a compliant management bean."); + } + return poolBeans; + } + + /** + * Returns the memory manager beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of memory manager beans, one for each manager. + */ + public static List getMemoryManagerMXBeans() + { + List managerBeans = new ArrayList(); + String[] names = VMManagementFactory.getMemoryManagerNames(); + for (int a = 0; a < names.length; ++a) + try + { + managerBeans.add(new MemoryManagerMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory manager bean, " + a + ", is " + + "not a compliant management bean."); + } + managerBeans.addAll(getGarbageCollectorMXBeans()); + return managerBeans; + } + + /** + * Returns the garbage collector beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of garbage collector beans, one for each pool. + */ + public static List getGarbageCollectorMXBeans() + { + List gcBeans = new ArrayList(); + String[] names = VMManagementFactory.getGarbageCollectorNames(); + for (int a = 0; a < names.length; ++a) + try + { + gcBeans.add(new GarbageCollectorMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "garbage collector bean, " + a + + ", is not a compliant management " + + "bean."); + } + return gcBeans; + } + +} diff --git a/libjava/classpath/java/lang/management/ManagementPermission.java b/libjava/classpath/java/lang/management/ManagementPermission.java new file mode 100644 index 00000000000..0e6bd06126f --- /dev/null +++ b/libjava/classpath/java/lang/management/ManagementPermission.java @@ -0,0 +1,132 @@ +/* ManagementPermission.java - Permissions for system management. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.security.BasicPermission; + +/** + * <p> + * Represents the permission to view or modify the data + * which forms part of the system management interfaces. + * Calls to methods of the system management beans, + * provided by the {@link ManagementFactory}, may perform + * checks against the current {@link java.lang.SecurityManager} + * (if any) before allowing the operation to proceed. + * Instances of this object are supplied to the + * {@link java.lang.SecurityManager} in order to perform + * these checks. It is not normal for instances of this + * class to be created outside the use of the + * {@link java.lang.SecurityManager}. + * </p> + * <p> + * This object can represent two types of management + * permission: + * </p> + * <ul> + * <li><strong>monitor</strong> — this allows access + * to information such as the arguments supplied to the + * virtual machine, the currently loaded classes and the + * stack traces of running threads. Malicious code may + * use this to obtain information about the system and + * exploit any vulnerabilities found.</li> + * <li><strong>control</strong> — this allows the + * information stored by the management beans to be altered. + * For example, additional debugging information (such + * as class loading traces) may be turned on or memory + * usage limits changed. Malicious code could use + * this to alter the behaviour of the system.</li> + * </ul> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public final class ManagementPermission + extends BasicPermission +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 1897496590799378737L; + + /** + * Constructs a new <code>ManagementPermission</code> + * for one of the two permission targets, "monitor" + * and "control". + * + * @param name the name of the permission this instance + * should represent; either "monitor" or + * "control". + * @throws IllegalArgumentException if the name is not + * either "monitor" + * or "control". + */ + public ManagementPermission(String name) + { + super(name); + if (!(name.equals("monitor") || name.equals("control"))) + throw new IllegalArgumentException("Invalid permission."); + } + + /** + * Constructs a new <code>ManagementPermission</code> + * for one of the two permission targets, "monitor" + * and "control". Actions are not supported, so + * this value should be either <code>null</code> + * or the empty string. + * + * @param name the name of the permission this instance + * should represent; either "monitor" or + * "control". + * @param actions either <code>null</code> or the + * empty string. + * @throws IllegalArgumentException if the name is not + * either "monitor" + * or "control", or + * a value for actions + * is specified. + */ + public ManagementPermission(String name, String actions) + { + this(name); + if (!(actions == null || actions.equals(""))) + throw new IllegalArgumentException("Invalid actions."); + } + +} + diff --git a/libjava/classpath/java/lang/management/MemoryMXBean.java b/libjava/classpath/java/lang/management/MemoryMXBean.java new file mode 100644 index 00000000000..4d9e5ff1cfd --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryMXBean.java @@ -0,0 +1,172 @@ +/* MemoryMXBean.java - Interface for a memory bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * <p> + * Provides access to information about the memory used + * by the virtual machine. An instance of this bean is + * obtained by calling + * {@link ManagementFactory#getMemoryMXBean()}. + * </p> + * <p> + * The Java virtual machine uses two types of memory: + * heap memory and non-heap memory. The heap is the + * storage location for class and array instances, and is + * thus the main source of memory associated with running + * Java programs. The heap is created when the virtual + * machine is started, and is periodically scanned by the + * garbage collector(s), in order to reclaim memory which + * is no longer used (e.g. because an object reference has + * gone out of scope). + * </p> + * <p> + * Non-heap memory is used by the virtual machine in order to + * perform its duties. Thus, it mainly acts as the storage + * location for structures created as a result of parsing Java + * bytecode, such as the constant pool and constructor/method + * declarations. When a Just-In-Time (JIT) compiler is in + * operation, this will use non-heap memory to store compiled + * bytecode. + * </p> + * <p> + * Both types of memory may be non-contiguous. During the + * lifetime of the virtual machine, the size of both may + * either change (either expanding or contracting) or stay + * the same. + * </p> + * <h2>Notifications</h2> + * <p> + * Implementations of this interface also conform to the + * {@link javax.management.NotificationEmitter} interface, + * and supply two notifications reflecting memory usage. + * These notifications occur when a usage threshold is + * exceeded; for more details of these, see the documentation + * of {@link MemoryPoolMXBean}. If threshold monitoring + * is supported, then a notification will be emitted each time + * the threshold is crossed. Another notification will not + * be emitted unless the usage level has dropped below the + * threshold again in the meantime. + * </p> + * <p> + * The emitted notifications are instances of + * {@link javax.management.Notification}, with a type of + * either + * {@link java.lang.management.MemoryNotificationInfo#MEMORY_THRESHOLD_EXCEEDED} + * or + * {@link java.lang.management.MemoryNotificationInfo#MEMORY_COLLECTION_THRESHOLD_EXCEEDED} + * (depending on whether the notification refers to the general + * usage threshold or the garbage collection threshold) and an instance + * of {@link java.lang.management.MemoryNotificationInfo} contained + * in the user data section. This is wrapped inside an instance + * of {@link javax.management.openmbean.CompositeData}, as explained + * in the documentation for + * {@link java.lang.management.MemoryNotificationInfo}. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryMXBean +{ + + /** + * Instigates a garbage collection cycle. The virtual + * machine's garbage collector should make the best + * attempt it can at reclaiming unused memory. This + * is equivalent to invoking {@link java.lang.System.gc()}. + * + * @see java.lang.System#gc() + */ + void gc(); + + /** + * Returns a {@link MemoryUsage} object representing the + * current state of the heap. This incorporates various + * statistics on both the initial and current memory + * allocations used by the heap. + * + * @return a {@link MemoryUsage} object for the heap. + */ + MemoryUsage getHeapMemoryUsage(); + + /** + * Returns a {@link MemoryUsage} object representing the + * current state of non-heap memory. This incorporates + * various statistics on both the initial and current + * memory allocations used by non-heap memory.. + * + * @return a {@link MemoryUsage} object for non-heap + * memory. + */ + MemoryUsage getNonHeapMemoryUsage(); + + /** + * Returns the number of objects which are waiting to + * be garbage collected (finalized). An object is + * finalized when the garbage collector determines that + * there are no more references to that object are in + * use. + * + * @return the number of objects awaiting finalization. + */ + int getObjectPendingFinalizationCount(); + + /** + * Returns true if the virtual machine will emit additional + * information when memory is allocated and deallocated. The + * format of the output is left up to the virtual machine. + * + * @return true if verbose memory output is on. + */ + boolean isVerbose(); + + /** + * Turns on or off the emission of additional information + * when memory is allocated and deallocated. The format of the + * output is left up to the virtual machine. This method + * may be called by multiple threads concurrently, but there + * is only one global setting of verbosity that is affected. + * + * @param verbose the new setting for verbose memory output. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void setVerbose(boolean verbose); + +} diff --git a/libjava/classpath/java/lang/management/MemoryManagerMXBean.java b/libjava/classpath/java/lang/management/MemoryManagerMXBean.java new file mode 100644 index 00000000000..9210705394e --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryManagerMXBean.java @@ -0,0 +1,77 @@ +/* MemoryManagerMXBean.java - Interface for a memory manager bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the memory managers + * of the virtual machine. An instance of this bean for each + * memory manager is obtained by calling + * {@link ManagementFactory#getMemoryManagerMXBeans()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryManagerMXBean +{ + + /** + * Returns an array containing the names of the memory pools + * this memory manager manages. + * + * @return an array containing the name of each memory pool + * this manager is responsible for. + */ + String[] getMemoryPoolNames(); + + /** + * Returns the name of the memory manager. + * + * @return the memory manager name. + */ + String getName(); + + /** + * Returns true if this memory manager is still valid. A memory + * manager becomes invalid when it is removed by the virtual machine + * and no longer used. + * + * @return true if this memory manager is valid. + */ + boolean isValid(); + +} diff --git a/libjava/classpath/java/lang/management/MemoryNotificationInfo.java b/libjava/classpath/java/lang/management/MemoryNotificationInfo.java new file mode 100644 index 00000000000..adb38b06fd7 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryNotificationInfo.java @@ -0,0 +1,215 @@ +/* MemoryNotificationInfo.java - Emitted memory notification info. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import gnu.java.lang.management.MemoryMXBeanImpl; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; + +/** + * <p> + * Represents the content of a notification emitted by the + * {@link MemoryMXBean}. Such notifications are emitted when + * one of the memory pools exceeds its usage or collection + * usage threshold. This object contains the following information, + * representing the state of the pool at the time of the + * notification: + * </p> + * <ul> + * <li>The name of the pool.</li> + * <li>The memory usage of the pool at the time of notification.</li> + * <li>The number of times the pool has exceeded this particular + * threshold in the past.</li> + * </ul> + * <p> + * Two types of notification are emitted by the {@link MemoryMXBean}: + * one for exceeding the usage threshold and one for exceeding the + * collection usage threshold. The value returned by {@link #getCount()} + * is dependent on this type; if the threshold exceeded is the usage + * threshold, then the usage threshold count is returned. If, instead, + * the collection usage threshold is exceeded, then the collection usage + * threshold count is returned. + * </p> + * <p> + * This data is held in the user data part of the notification (returned + * by {@link javax.management.Notification#getUserData()}) encapsulated in + * a {@link javax.management.openmbean.CompositeData} object. The + * {@link #from(javax.management.openmbean.CompositeData)} method may be + * used to unwrap the value and obtain an instance of this class. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MemoryNotificationInfo +{ + + /** + * The type of notification emitted when the usage threshold is exceeded. + * After a notification is emitted, the usage level must drop below the + * threshold again before another notification is emitted. The value is + * <code>java.management.memory.threshold.exceeded</code>. + */ + public static final String MEMORY_THRESHOLD_EXCEEDED = + "java.management.memory.threshold.exceeded"; + + /** + * The type of notification emitted when the collection usage threshold + * is exceeded, following a garbage collection cycle. The value is + * <code>java.management.memory.collection.threshold.exceeded</code>. + */ + public static final String MEMORY_COLLECTION_THRESHOLD_EXCEEDED = + "java.management.memory.collection.threshold.exceeded"; + + /** + * The name of the memory pool which exceeded the threshold. + */ + private String poolName; + + /** + * The usage level of the memory pool at the time of notification. + */ + private MemoryUsage usage; + + /** + * The number of times the threshold has been crossed. + */ + private long count; + + /** + * Constructs a new {@link MemoryNotificationInfo} object using the + * specified pool name, usage level and threshold crossing count. + * + * @param poolName the name of the pool which has exceeded a threshold. + * @param usage the usage level of the pool at the time of notification. + * @param count the number of times the threshold has been crossed. + */ + public MemoryNotificationInfo(String poolName, MemoryUsage usage, long count) + { + this.poolName = poolName; + this.usage = usage; + this.count = count; + } + + /** + * <p> + * Returns a {@link MemoryNotificationInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>poolName</td><td>java.lang.String</td></tr> + * <tr><td>usage</td><td>javax.management.openmbean.CompositeData + * </td></tr> + * <tr><td>count</td><td>java.lang.Long</td></tr> + * </table> + * <p> + * The usage level is further described as: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>init</td><td>java.lang.Long</td></tr> + * <tr><td>used</td><td>java.lang.Long</td></tr> + * <tr><td>committed</td><td>java.lang.Long</td></tr> + * <tr><td>max</td><td>java.lang.Long</td></tr> + * </table> + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or <code>null</code> + * if the data structure was also <code>null</code>. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static MemoryNotificationInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "poolName", SimpleType.STRING); + ThreadInfo.checkAttribute(type, "usage", MemoryMXBeanImpl.usageType); + ThreadInfo.checkAttribute(type, "count", SimpleType.LONG); + MemoryUsage usage = MemoryUsage.from((CompositeData) data.get("usage")); + return new MemoryNotificationInfo(((String) data.get("poolName")), + usage, + ((Long) data.get("count")).longValue()); + } + + /** + * Returns the number of times the memory pool has crossed the usage + * threshold, as of the time of notification. If this is the notification + * represented by the type {@link #MEMORY_THRESHOLD_EXCEEDED}, then the + * count is the usage threshold count. If this is the notification + * represented by the type {@link #MEMORY_COLLECTION_THRESHOLD_EXCEEDED}, + * then the count is the collection usage threshold count. + * + * @return the number of times the appropriate threshold has been crossed. + */ + public long getCount() + { + return count; + } + + /** + * Returns the name of the pool which has crossed a threshold. + * + * @return the name of the pool. + */ + public String getPoolName() + { + return poolName; + } + + /** + * Returns the usage levels at the time of notification. + * + * @return the usage levels. + */ + public MemoryUsage getUsage() + { + return usage; + } + +} + diff --git a/libjava/classpath/java/lang/management/MemoryPoolMXBean.java b/libjava/classpath/java/lang/management/MemoryPoolMXBean.java new file mode 100644 index 00000000000..5b04c64d3b8 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryPoolMXBean.java @@ -0,0 +1,318 @@ +/* MemoryPoolMXBean.java - Interface for a memory pool bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * <p> + * Provides access to information about one of the memory + * resources or pools used by the virtual machine. Instances + * of this bean are obtained by calling + * {@link ManagementFactory#getMemoryPoolMXBeans()}. One + * bean is returned for each memory pool provided. + * </p> + * <p> + * The memory pool bean allows the usage of the pool to be + * monitored. The bean can provide statistics on the current + * and peak usage of the pool, and on the threshold levels the + * pool uses. + * </p> + * <p> + * {@link getUsage()} returns an approximation of the current + * usage of the pool. Calls to this method are expected to be + * generally quick to perform; if the call is expensive, the + * documentation of the bean should specify so. For memory + * pool beans that represent the memory used by garbage + * collectors, the usage level includes both referenced and + * unreferenced objects. + * </p> + * <p> + * {@link getPeakUsage()} and {@link resetPeakUsage()} enable + * the retrieval of the peak usage level and setting it to the + * current usage level, respectively. Initially, the peak usage + * level is relative to the start of the virtual machine. + * </p> + * <p> + * Memory pools may also include optional support for usage thresholds. + * The usage threshold is a particular level of memory usage. When this + * value is crossed (the current memory usage becomes equal to or greater + * than this threshold level), the usage threshold count is increased. + * This feature is designed for monitoring the trend in memory usage. + * Support for a collection usage threshold is also provided, for + * particular garbage collectors. This is used to monitor the amount + * of memory left uncollected after a garbage collection cycle. There + * is no need to make special garbage collection runs to support this; + * the level following collection just needs to be monitored. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryPoolMXBean +{ + + /** + * Returns memory usage statistics after a best-effort attempt + * has been made to remove unused objects from the pool. This + * method is designed for use by the pools of garbage collectors, + * in order to monitor the amount of memory used after collections. + * It will return <code>null</code> if such functionality is + * unsupported by the memory pool represented by this bean. + * + * @return the memory usage of the memory pool after the most + * recent garbage collection cycle, or <code>null</code> + * if this operation is not supported. + */ + MemoryUsage getCollectionUsage(); + + /** + * Returns the collection usage threshold level in bytes. This + * value is initially zero. + * + * @return the collection usage threshold in bytes. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + long getCollectionUsageThreshold(); + + /** + * Returns the number of times the usage level has matched or + * exceeded the collection usage threshold. + * + * @return the number of times the usage level has matched + * or exceeded the collection usage threshold. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThreshold() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + long getCollectionUsageThresholdCount(); + + /** + * Returns the names of the memory managers associated with this + * pool. Each pool has at least one memory manager. + * + * @return an array containing the name of each memory manager + * responsible for this pool. + */ + String[] getMemoryManagerNames(); + + /** + * Returns the name of the memory pool. + * + * @return the memory pool name. + */ + String getName(); + + /** + * Returns memory usage statistics for the peak memory usage + * of the pool. The peak is the maximum memory usage occurring + * since the virtual machine was started or since the peak + * was reset by {@link #resetPeakUsage()}. The return value + * may be <code>null</code> if this pool is no longer valid. + * + * @return the memory usage of the memory pool at its peak, + * or <code>null</code> if this pool is no longer valid. + */ + MemoryUsage getPeakUsage(); + + /** + * Returns the type of memory used by this pool. This can be + * either heap or non-heap memory. + * + * @return the type of this pool. + */ + String getType(); + + /** + * Returns memory usage statistics for the current memory usage + * of the pool. The return value may be <code>null</code> if + * this pool is no longer valid. Obtaining these values is + * expected to be a relatively quick operation; if this will + * instead be an expensive operation to perform, the documentation + * of the implementating bean should specify that this is the + * case. The values are intended to be an estimate for monitoring + * purposes. + * + * @return the memory usage of the memory pool at present, + * or <code>null</code> if this pool is no longer valid. + */ + MemoryUsage getUsage(); + + /** + * Returns the usage threshold level in bytes. This + * value is initially defined by the virtual machine. + * + * @return the usage threshold in bytes. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + long getUsageThreshold(); + + /** + * Returns the number of times the usage level has matched or + * exceeded the usage threshold. + * + * @return the number of times the usage level has matched + * or exceeded the usage threshold. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThreshold() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + long getUsageThresholdCount(); + + /** + * Returns true if the collection usage level is equal to + * or greater than the collection usage threshold. + * + * @return true if the collection usage threshold has been + * matched or exceeded. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + boolean isCollectionUsageThresholdExceeded(); + + /** + * Returns true if this memory pool supports a collection usage + * level threshold. + * + * @return true if a collection usage level threshold is supported. + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #setCollectionUsageThreshold(long) + */ + boolean isCollectionUsageThresholdSupported(); + + /** + * Returns true if the usage level is equal to + * or greater than the usage threshold. + * + * @return true if the usage threshold has been + * matched or exceeded. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + boolean isUsageThresholdExceeded(); + + /** + * Returns true if this memory pool supports a usage level threshold. + * + * @return true if a usage level threshold is supported. + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #setUsageThreshold(long) + */ + boolean isUsageThresholdSupported(); + + /** + * Returns true if this memory pool is still valid. A memory pool + * becomes invalid when it is removed by the virtual machine and + * no longer used. + * + * @return true if this memory pool is valid. + */ + boolean isValid(); + + /** + * Resets the peak memory usage level to the current memory usage + * level. + * + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void resetPeakUsage(); + + /** + * Sets the collection threshold usage level to the given value. + * A value of zero disables the collection threshold. + * + * @param threshold the new threshold level. + * @throws IllegalArgumentException if the threshold hold level + * is negative. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + */ + void setCollectionUsageThreshold(long threshold); + + /** + * Sets the threshold usage level to the given value. A value of + * zero disables the threshold. + * + * @param threshold the new threshold level. + * @throws IllegalArgumentException if the threshold hold level + * is negative. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + */ + void setUsageThreshold(long threshold); + +} diff --git a/libjava/classpath/java/lang/management/MemoryUsage.java b/libjava/classpath/java/lang/management/MemoryUsage.java new file mode 100644 index 00000000000..3c2a4cba6e9 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryUsage.java @@ -0,0 +1,265 @@ +/* MemoryUsage.java - Information on the usage of a memory pool. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; +/** + * <p> + * Retains information on the usage of a particular memory + * pool, or heap/non-heap memory as a whole. Memory usage + * is represented by four values (all in bytes): + * </p> + * <ul> + * <li><strong>Initial Level</strong>: This is the initial + * amount of memory allocated for the pool by the operating + * system. This value may be undefined.</li> + * <li><strong>Used Level</strong>: This is the amount of + * memory currently in use.</li> + * <li><strong>Committed Level</strong>: This is the current + * amount of memory allocated for the pool by the operating + * system. This value will always be equal to or greater than + * the current amount of memory in use. It may drop below + * the initial amount, if the virtual machine judges this to + * be practical.</li> + * <li><strong>Maximum Level</strong>: This is the maximum + * amount of memory that may be allocated for the pool by + * the operating system. Like the initial amount, it may + * be undefined. If it is defined, it will be greater than + * or equal to the used and committed amounts and may change + * over time. It is not guaranteed that the maximum amount + * of memory may actually be allocated to the pool. For + * example, a request for an amount of memory greater than + * the current committed level, but less than the maximum, + * may still fail due to resources at the operating system + * level not being sufficient to fulfill the demand.</li> + * </ul> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + * @see MemoryMXBean + * @see MemoryPoolMXBean + */ +public class MemoryUsage +{ + + /** + * The initial amount of memory allocated. + */ + private long init; + + /** + * The amount of memory used. + */ + private long used; + + /** + * The amount of memory committed for use. + */ + private long committed; + + /** + * The maximum amount of memory available. + */ + private long maximum; + + /** + * Constructs a new {@link MemoryUsage} object with + * the specified allocation levels. + * + * @param init the initial amount of memory allocated, + * or -1 if this value is undefined. Must + * be >= -1. + * @param used the amount of memory used. Must be >= 0, + * and <= committed. + * @param committed the amount of memory committed for use + * at present. Must be >= 0 and <= + * maximum (if defined). + * @param maximum the maximum amount of memory that may be + * used, or -1 if this value is undefined. + * Must be >= -1. + * @throws IllegalArgumentException if the values break any + * of the limits specified + * above. + */ + public MemoryUsage(long init, long used, long committed, + long maximum) + { + if (init < -1) + throw new IllegalArgumentException("Initial value of " + + init + " is too small."); + if (used < 0) + throw new IllegalArgumentException("Used value of " + + used + " is too small."); + if (committed < 0) + throw new IllegalArgumentException("Committed value of " + + committed + " is too small."); + if (committed < used) + throw new IllegalArgumentException("Committed value of " + + committed + " is below " + + used + ", the amount used."); + if (maximum < -1) + throw new IllegalArgumentException("Maximum value of " + + maximum + " is too small."); + if (maximum != -1 && maximum < committed) + throw new IllegalArgumentException("Maximum value of " + + maximum + " is below " + + committed + ", the amount " + + "committed."); + this.init = init; + this.used = used; + this.committed = committed; + this.maximum = maximum; + } + + /** + * <p> + * Returns a {@link MemoryUsage} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes: + * </p> + * <ul> + * <li>init</li> + * <li>used</li> + * <li>committed</li> + * <li>max</li> + * </ul> + * <p> + * All should have the type, <code>java.lang.Long</code>. + * </p> + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or <code>null</code> + * if the data structure was also <code>null</code>. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above, or the values + * are invalid. + */ + public static MemoryUsage from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "init", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "used", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "committed", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "max", SimpleType.LONG); + return new MemoryUsage(((Long) data.get("init")).longValue(), + ((Long) data.get("used")).longValue(), + ((Long) data.get("committed")).longValue(), + ((Long) data.get("max")).longValue()); + } + + /** + * Returns the amount of memory committed for use by this + * memory pool (in bytes). This amount is guaranteed to + * be available, unlike the maximum. + * + * @return the committed amount of memory. + */ + public long getCommitted() + { + return committed; + } + + /** + * Returns the initial amount of memory allocated to the + * pool (in bytes). This method may return -1, if the + * value is undefined. + * + * @return the initial amount of memory allocated, or -1 + * if this value is undefined. + */ + public long getInit() + { + return init; + } + + /** + * Returns the maximum amount of memory available for this + * pool (in bytes). This amount is not guaranteed to + * actually be usable. This method may return -1, if the + * value is undefined. + * + * @return the maximum amount of memory available, or -1 + * if this value is undefined. + */ + public long getMax() + { + return maximum; + } + + /** + * Returns the amount of memory used (in bytes). + * + * @return the amount of used memory. + */ + public long getUsed() + { + return used; + } + + /** + * Returns a {@link java.lang.String} representation of + * this {@link MemoryUsage} object. This takes the form + * <code>java.lang.management.MemoryUsage[init=i, used=u, + * committed=c, maximum=m]</code>, where <code>i</code> + * is the initial level, <code>u</code> is the used level, + * <code>c</code> is the committed level and <code>m</code> + * is the maximum level. + * + * @return the string specified above. + */ + public String toString() + { + int megabyte = 1024 * 1024; + return getClass().getName() + + "[init=" + init + " bytes (~" + (init / megabyte) + + "MB), used=" + used + " bytes (~" + (used / megabyte) + + "MB), committed=" + committed + " bytes (~" + (committed / megabyte) + + "MB), maximum=" + maximum + " bytes (~" + (maximum / megabyte) + + "MB)]"; + } + +} + diff --git a/libjava/classpath/java/lang/management/OperatingSystemMXBean.java b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java new file mode 100644 index 00000000000..2430a9fbf5c --- /dev/null +++ b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java @@ -0,0 +1,103 @@ +/* OperatingSystemMXBean.java - Interface for an operating system bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the underlying operating + * system. An instance of this bean is obtained by calling + * {@link ManagementFactory#getOperatingSystemMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface OperatingSystemMXBean +{ + + /** + * Returns the name of the underlying system architecture. This + * is equivalent to obtaining the <code>os.arch</code> property + * via {@link System#getProperty(String)}. + * + * @return the name of the underlying system architecture on which + * the VM is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getArch(); + + /** + * Returns the number of processors currently available to the + * virtual machine. This number is subject to change during + * execution of the virtual machine, and will always be >= 1. + * The call is equivalent to {@link Runtime#availableProcessors()}. + * + * @return the number of processors available to the VM. + */ + int getAvailableProcessors(); + + /** + * Returns the name of the underlying operating system. This + * is equivalent to obtaining the <code>os.name</code> property + * via {@link System#getProperty(String)}. + * + * @return the name of the operating system on which the VM + * is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getName(); + + /** + * Returns the version of the underlying operating system. This + * is equivalent to obtaining the <code>os.version</code> property + * via {@link System#getProperty(String)}. + * + * @return the version of the operating system on which the VM + * is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVersion(); + +} diff --git a/libjava/classpath/java/lang/management/RuntimeMXBean.java b/libjava/classpath/java/lang/management/RuntimeMXBean.java new file mode 100644 index 00000000000..cee1d805f89 --- /dev/null +++ b/libjava/classpath/java/lang/management/RuntimeMXBean.java @@ -0,0 +1,278 @@ +/* RuntimeMXBean.java - Interface for a runtime bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.util.List; +import java.util.Map; + +/** + * Provides access to information about the underlying virtual + * machine. An instance of this bean is obtained by calling + * {@link ManagementFactory#getRuntimeMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface RuntimeMXBean +{ + + /** + * <p> + * Returns the boot classpath used by the virtual machine. This + * value follows the standard path syntax used by the underlying + * operating system (e.g. directories separated by ':' on UNIX + * or ';' on Windows). + * </p> + * <p> + * Supplying this value is optional. Users should check the + * return value of {@link isBootClassPathSupported()} prior to + * calling this method. + * </p> + * + * @return the boot classpath of the virtual machine, if supported. + * @throws UnsupportedOperationException in cases where this + * functionality is not + * supported by the VM. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @see #isBootClassPathSupported() + * @see java.lang.management.ManagementPermission + */ + String getBootClassPath(); + + /** + * Returns the classpath used by the system classloader. This + * is equivalent to obtaining the <code>java.class.path</code> + * property via {@link System#getProperty(String)}. This value + * follows the standard path syntax used by the underlying operating + * system (e.g. directories separated by ':' on UNIX or ';' on + * Windows). + * + * @return the classpath used by the system class loader. + * @throws SecurityException if a security manager exists which + * prevents access to the classpath + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getClassPath(); + + /** + * Returns a list of the arguments given to the virtual machine, + * excluding those that apply to the <code>main()</code> method + * of the class file being executed. These may not just be those + * specified at the command line, but may also include arguments + * from environment variables, configuration files, etc. All + * command line arguments may not reach the virtual machine, so + * these are not included in this list. + * + * @return a list of arguments passed to the virtual machine. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @see java.lang.management.ManagementPermission + */ + List getInputArguments(); + + /** + * Returns the library path. This is equivalent to obtaining the + * <code>java.library.path</code> property via + * {@link System#getProperty(String)}. This value follows the + * standard path syntax used by the underlying operating + * system (e.g. directories separated by ':' on UNIX or ';' on + * Windows). + * + * @return the library path. + * @throws SecurityException if a security manager exists which + * prevents access to the library path + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getLibraryPath(); + + /** + * Returns the version of the management specification + * implemented by the virtual machine. + * + * @return the version of the management specification + * implemented. + */ + String getManagementSpecVersion(); + + /** + * Returns the name of this virtual machine. The content + * of this property is left up to the developer of the + * virtual machine. It may include a number of system + * attributes and may differ between instances of the + * same virtual machine (for example, it might include + * the process identifier or the host name of the machine + * on which it is running). The intention is that this + * name refers to the precise entity that the other data + * supplied by this bean refers to, rather than the VM + * in general. + * + * @return the name of this virtual machine. + */ + String getName(); + + /** + * Returns the specification name of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.specification.name</code> property via + * {@link System#getProperty(String)}. + * + * @return the specification name of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecName(); + + /** + * Returns the specification vendor of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.specification.vendor</code> property via + * {@link System#getProperty(String)}. + * + * @return the specification vendor of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification vendor property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecVendor(); + + /** + * Returns the specification version of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.specification.version</code> property via + * {@link System#getProperty(String)}. + * + * @return the specification version of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification version property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecVersion(); + + /** + * Returns the approximate start time of the virtual machine + * in milliseconds. + * + * @return the start time of the virtual machine. + */ + long getStartTime(); + + /** + * Returns a map containing the keys and values of the system + * properties. This gives largely the same result as calling + * {@link System#getProperties()}, but the resulting map + * is filtered so as to only provide keys and values that + * are <code>String</code>s. + * + * @return the map of system properties. + */ + Map getSystemProperties(); + + /** + * Returns the uptime of the virtual machine in milliseconds. + * + * @return the uptime of the virtual machine. + */ + long getUptime(); + + /** + * Returns the implementation name of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.name</code> property via + * {@link System#getProperty(String)}. + * + * @return the implementation name of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM name + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmName(); + + /** + * Returns the implementation vendor of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.vendor</code> property via + * {@link System#getProperty(String)}. + * + * @return the implementation vendor of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM vendor + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmVendor(); + + /** + * Returns the implementation version of the virtual machine. + * This is equivalent to obtaining the + * <code>java.vm.version</code> property via + * {@link System#getProperty(String)}. + * + * @return the implementation version of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM version + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmVersion(); + + /** + * Returns true if the virtual machine supports the boot classpath + * mechanism. + * + * @return true if the boot classpath property is supported by the + * virtual machine. + */ + boolean isBootClassPathSupported(); + +} diff --git a/libjava/classpath/java/lang/management/ThreadInfo.java b/libjava/classpath/java/lang/management/ThreadInfo.java new file mode 100644 index 00000000000..4bf35a4cbeb --- /dev/null +++ b/libjava/classpath/java/lang/management/ThreadInfo.java @@ -0,0 +1,704 @@ +/* ThreadInfo.java - Information on a thread + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +/** + * <p> + * A class which maintains information about a particular + * thread. This information includes: + * </p> + * <ul> + * <li><strong>General Thread Information:</strong> + * <ul> + * <li>The identifier of the thread.</li> + * <li>The name of the thread.</li> + * </ul> + * </li> + * <li><strong>Execution Information:</strong> + * <ul> + * <li>The current state of the thread (e.g. blocked, runnable)</li> + * <li>The object upon which the thread is blocked, either because + * the thread is waiting to obtain the monitor of that object to enter + * one of its synchronized monitor, or because + * {@link java.lang.Object#wait()} has been called while the thread + * was within a method of that object.</li> + * <li>The thread identifier of the current thread holding an object's + * monitor, upon which the thread described here is blocked.</li> + * <li>The stack trace of the thread (if requested on creation + * of this object</li> + * </ul> + * <li><strong>Synchronization Statistics</strong> + * <ul> + * <li>The number of times the thread has been blocked waiting for + * an object's monitor or in a {@link java.lang.Object#wait()} call.</li> + * <li>The accumulated time the thread has been blocked waiting for + * an object's monitor on in a {@link java.lang.Object#wait()} call. + * The availability of these statistics depends on the virtual machine's + * support for thread contention monitoring (see + * {@link ThreadMXBean#isThreadContentionMonitoringSupported()}.</li> + * </ul> + * </li> + * </ul> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ +public class ThreadInfo +{ + + /** + * The id of the thread which this instance concerns. + */ + private long threadId; + + /** + * The name of the thread which this instance concerns. + */ + private String threadName; + + /** + * The state of the thread which this instance concerns. + */ + private String threadState; + + /** + * The number of times the thread has been blocked. + */ + private long blockedCount; + + /** + * The accumulated number of milliseconds the thread has + * been blocked (used only with thread contention monitoring + * support). + */ + private long blockedTime; + + /** + * The name of the monitor lock on which this thread + * is blocked (if any). + */ + private String lockName; + + /** + * The id of the thread which owns the monitor lock on + * which this thread is blocked, or <code>-1</code> + * if there is no owner. + */ + private long lockOwnerId; + + /** + * The name of the thread which owns the monitor lock on + * which this thread is blocked, or <code>null</code> + * if there is no owner. + */ + private String lockOwnerName; + + /** + * The number of times the thread has been in a waiting + * state. + */ + private long waitedCount; + + /** + * The accumulated number of milliseconds the thread has + * been waiting (used only with thread contention monitoring + * support). + */ + private long waitedTime; + + /** + * True if the thread is in a native method. + */ + private boolean isInNative; + + /** + * True if the thread is suspended. + */ + private boolean isSuspended; + + /** + * The stack trace of the thread. + */ + private StackTraceElement[] trace; + + /** + * Cache a local reference to the thread management bean. + */ + private static ThreadMXBean bean = null; + + /** + * Constructs a new {@link ThreadInfo} corresponding + * to the thread specified. + * + * @param thread the thread on which the new instance + * will be based. + * @param blockedCount the number of times the thread + * has been blocked. + * @param blockedTime the accumulated number of milliseconds + * the specified thread has been blocked + * (only used with contention monitoring enabled) + * @param lock the monitor lock the thread is waiting for + * (only used if blocked) + * @param lockOwner the thread which owns the monitor lock, or + * <code>null</code> if it doesn't have an owner + * (only used if blocked) + * @param waitedCount the number of times the thread has been in a + * waiting state. + * @param waitedTime the accumulated number of milliseconds the + * specified thread has been waiting + * (only used with contention monitoring enabled) + * @param isInNative true if the thread is in a native method. + * @param isSuspended true if the thread is suspended. + * @param trace the stack trace of the thread to a pre-determined + * depth (see VMThreadMXBeanImpl) + */ + private ThreadInfo(Thread thread, long blockedCount, long blockedTime, + Object lock, Thread lockOwner, long waitedCount, + long waitedTime, boolean isInNative, boolean isSuspended, + StackTraceElement[] trace) + { + this(thread.getId(), thread.getName(), thread.getState(), blockedCount, + blockedTime, lock.getClass().getName() + "@" + + Integer.toHexString(System.identityHashCode(lock)), lockOwner.getId(), + lockOwner.getName(), waitedCount, waitedTime, isInNative, isSuspended, + trace); + } + + /** + * Constructs a new {@link ThreadInfo} corresponding + * to the thread details specified. + * + * @param threadId the id of the thread on which this + * new instance will be based. + * @param threadName the name of the thread on which + * this new instance will be based. + * @param threadState the state of the thread on which + * this new instance will be based. + * @param blockedCount the number of times the thread + * has been blocked. + * @param blockedTime the accumulated number of milliseconds + * the specified thread has been blocked + * (only used with contention monitoring enabled) + * @param lockName the name of the monitor lock the thread is waiting for + * (only used if blocked) + * @param lockOwnerId the id of the thread which owns the monitor + * lock, or <code>-1</code> if it doesn't have an owner + * (only used if blocked) + * @param lockOwnerName the name of the thread which owns the monitor + * lock, or <code>null</code> if it doesn't have an + * owner (only used if blocked) + * @param waitedCount the number of times the thread has been in a + * waiting state. + * @param waitedTime the accumulated number of milliseconds the + * specified thread has been waiting + * (only used with contention monitoring enabled) + * @param isInNative true if the thread is in a native method. + * @param isSuspended true if the thread is suspended. + * @param trace the stack trace of the thread to a pre-determined + * depth (see VMThreadMXBeanImpl) + */ + private ThreadInfo(long threadId, String threadName, String threadState, + long blockedCount, long blockedTime, String lockName, + long lockOwnerId, String lockOwnerName, long waitedCount, + long waitedTime, boolean isInNative, boolean isSuspended, + StackTraceElement[] trace) + { + this.threadId = threadId; + this.threadName = threadName; + this.threadState = threadState; + this.blockedCount = blockedCount; + this.blockedTime = blockedTime; + this.lockName = lockName; + this.lockOwnerId = lockOwnerId; + this.lockOwnerName = lockOwnerName; + this.waitedCount = waitedCount; + this.waitedTime = waitedTime; + this.isInNative = isInNative; + this.isSuspended = isSuspended; + this.trace = trace; + } + + /** + * Checks for an attribute in a {@link CompositeData} structure + * with the correct type. + * + * @param ctype the composite data type to check. + * @param name the name of the attribute. + * @param type the type to check for. + * @throws IllegalArgumentException if the attribute is absent + * or of the wrong type. + */ + static void checkAttribute(CompositeType ctype, String name, + OpenType type) + throws IllegalArgumentException + { + OpenType foundType = ctype.getType(name); + if (foundType == null) + throw new IllegalArgumentException("Could not find a field named " + + name); + if (!(foundType.equals(type))) + throw new IllegalArgumentException("Field " + name + " is not of " + + "type " + type.getClassName()); + } + + /** + * <p> + * Returns a {@link ThreadInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>threadId</td><td>java.lang.Long</td></tr> + * <tr><td>threadName</td><td>java.lang.String</td></tr> + * <tr><td>threadState</td><td>java.lang.String</td></tr> + * <tr><td>suspended</td><td>java.lang.Boolean</td></tr> + * <tr><td>inNative</td><td>java.lang.Boolean</td></tr> + * <tr><td>blockedCount</td><td>java.lang.Long</td></tr> + * <tr><td>blockedTime</td><td>java.lang.Long</td></tr> + * <tr><td>waitedCount</td><td>java.lang.Long</td></tr> + * <tr><td>waitedTime</td><td>java.lang.Long</td></tr> + * <tr><td>lockName</td><td>java.lang.String</td></tr> + * <tr><td>lockOwnerId</td><td>java.lang.Long</td></tr> + * <tr><td>lockOwnerName</td><td>java.lang.String</td></tr> + * <tr><td>stackTrace</td><td>javax.management.openmbean.CompositeData[] + * </td></tr> + * </table> + * <p> + * The stack trace is further described as: + * </p> + * <table> + * <th><td>Name</td><td>Type</td></th> + * <tr><td>className</td><td>java.lang.String</td></tr> + * <tr><td>methodName</td><td>java.lang.String</td></tr> + * <tr><td>fileName</td><td>java.lang.String</td></tr> + * <tr><td>lineNumber</td><td>java.lang.Integer</td></tr> + * <tr><td>nativeMethod</td><td>java.lang.Boolean</td></tr> + * </table> + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or <code>null</code> + * if the data structure was also <code>null</code>. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static ThreadInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + checkAttribute(type, "threadId", SimpleType.LONG); + checkAttribute(type, "threadName", SimpleType.STRING); + checkAttribute(type, "threadState", SimpleType.STRING); + checkAttribute(type, "suspended", SimpleType.BOOLEAN); + checkAttribute(type, "inNative", SimpleType.BOOLEAN); + checkAttribute(type, "blockedCount", SimpleType.LONG); + checkAttribute(type, "blockedTime", SimpleType.LONG); + checkAttribute(type, "waitedCount", SimpleType.LONG); + checkAttribute(type, "waitedTime", SimpleType.LONG); + checkAttribute(type, "lockName", SimpleType.STRING); + checkAttribute(type, "lockOwnerId", SimpleType.LONG); + checkAttribute(type, "lockOwnerName", SimpleType.STRING); + try + { + CompositeType seType = + new CompositeType(StackTraceElement.class.getName(), + "An element of a stack trace", + new String[] { "className", "methodName", + "fileName", "lineNumber", + "nativeMethod" + }, + new String[] { "Name of the class", + "Name of the method", + "Name of the source code file", + "Line number", + "True if this is a native method" + }, + new OpenType[] { + SimpleType.STRING, SimpleType.STRING, + SimpleType.STRING, SimpleType.INTEGER, + SimpleType.BOOLEAN + }); + checkAttribute(type, "stackTrace", new ArrayType(1, seType)); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "stack trace element.", e); + } + CompositeData[] dTraces = (CompositeData[]) data.get("stackTrace"); + StackTraceElement[] traces = new StackTraceElement[dTraces.length]; + for (int a = 0; a < dTraces.length; ++a) + /* FIXME: We can't use the boolean as there is no available + constructor. */ + traces[a] = + new StackTraceElement((String) dTraces[a].get("className"), + (String) dTraces[a].get("methodName"), + (String) dTraces[a].get("fileName"), + ((Integer) + dTraces[a].get("lineNumber")).intValue()); + return new ThreadInfo(((Long) data.get("threadId")).longValue(), + (String) data.get("threadName"), + (String) data.get("threadState"), + ((Long) data.get("blockedCount")).longValue(), + ((Long) data.get("blockedTime")).longValue(), + (String) data.get("lockName"), + ((Long) data.get("lockOwnerId")).longValue(), + (String) data.get("lockOwnerName"), + ((Long) data.get("waitedCount")).longValue(), + ((Long) data.get("waitedTime")).longValue(), + ((Boolean) data.get("inNative")).booleanValue(), + ((Boolean) data.get("suspended")).booleanValue(), + traces); + } + + /** + * Returns the number of times this thread has been + * in the {@link java.lang.Thread.State#BLOCKED} state. + * A thread enters this state when it is waiting to + * obtain an object's monitor. This may occur either + * on entering a synchronized method for the first time, + * or on re-entering it following a call to + * {@link java.lang.Object#wait()}. + * + * @return the number of times this thread has been blocked. + */ + public long getBlockedCount() + { + return blockedCount; + } + + /** + * <p> + * Returns the accumulated number of milliseconds this + * thread has been in the + * {@link java.lang.Thread.State#BLOCKED} state + * since thread contention monitoring was last enabled. + * A thread enters this state when it is waiting to + * obtain an object's monitor. This may occur either + * on entering a synchronized method for the first time, + * or on re-entering it following a call to + * {@link java.lang.Object#wait()}. + * </p> + * <p> + * Use of this method requires virtual machine support + * for thread contention monitoring and for this support + * to be enabled. + * </p> + * + * @return the accumulated time (in milliseconds) that this + * thread has spent in the blocked state, since + * thread contention monitoring was enabled, or -1 + * if thread contention monitoring is disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see ThreadMXBean#isThreadContentionMonitoringEnabled() + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ + public long getBlockedTime() + { + if (bean == null) + bean = ManagementFactory.getThreadMXBean(); + // Will throw UnsupportedOperationException for us + if (bean.isThreadContentionMonitoringEnabled()) + return blockedTime; + else + return -1; + } + + /** + * <p> + * Returns a {@link java.lang.String} representation of + * the monitor lock on which this thread is blocked. If + * the thread is not blocked, this method returns + * <code>null</code>. + * </p> + * <p> + * The returned {@link java.lang.String} is constructed + * using the class name and identity hashcode (usually + * the memory address of the object) of the lock. The + * two are separated by the '@' character, and the identity + * hashcode is represented in hexadecimal. Thus, for a + * lock, <code>l</code>, the returned value is + * the result of concatenating + * <code>l.getClass().getName()</code>, <code>"@"</code> + * and + * <code>Integer.toHexString(System.identityHashCode(l))</code>. + * The value is only unique to the extent that the identity + * hash code is also unique. + * </p> + * + * @return a string representing the lock on which this + * thread is blocked, or <code>null</code> if + * the thread is not blocked. + */ + public String getLockName() + { + if (threadState.equals("BLOCKED")) + return null; + return lockName; + } + + /** + * Returns the identifier of the thread which owns the + * monitor lock this thread is waiting for. -1 is returned + * if either this thread is not blocked, or the lock is + * not held by any other thread. + * + * @return the thread identifier of thread holding the lock + * this thread is waiting for, or -1 if the thread + * is not blocked or the lock is not held by another + * thread. + */ + public long getLockOwnerId() + { + if (threadState.equals("BLOCKED")) + return -1; + return lockOwnerId; + } + + /** + * Returns the name of the thread which owns the + * monitor lock this thread is waiting for. <code>null</code> + * is returned if either this thread is not blocked, + * or the lock is not held by any other thread. + * + * @return the thread identifier of thread holding the lock + * this thread is waiting for, or <code>null</code> + * if the thread is not blocked or the lock is not + * held by another thread. + */ + public String getLockOwnerName() + { + if (threadState.equals("BLOCKED")) + return null; + return lockOwnerName; + } + + /** + * <p> + * Returns the stack trace of this thread to the depth + * specified on creation of this {@link ThreadInfo} + * object. If the depth is zero, an empty array will + * be returned. For non-zero arrays, the elements + * start with the most recent trace at position zero. + * The bottom of the stack represents the oldest method + * invocation which meets the depth requirements. + * </p> + * <p> + * Some virtual machines may not be able to return + * stack trace information for a thread. In these + * cases, an empty array will also be returned. + * </p> + * + * @return an array of {@link java.lang.StackTraceElement}s + * representing the trace of this thread. + */ + public StackTraceElement[] getStackTrace() + { + return trace; + } + + /** + * Returns the identifier of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's identifier. + */ + public long getThreadId() + { + return threadId; + } + + /** + * Returns the name of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's name. + */ + public String getThreadName() + { + return threadName; + } + + /** + * Returns the state of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's state. + */ + public String getThreadState() + { + return threadState; + } + + /** + * Returns the number of times this thread has been + * in the {@link java.lang.Thread.State#WAITING} + * or {@link java.lang.Thread.State#TIMED_WAITING} state. + * A thread enters one of these states when it is waiting + * due to a call to {@link java.lang.Object.wait()}, + * {@link java.lang.Object.join()} or + * {@link java.lang.concurrent.locks.LockSupport.park()}, + * either with an infinite or timed delay, respectively. + * + * @return the number of times this thread has been waiting. + */ + public long getWaitedCount() + { + return waitedCount; + } + + /** + * <p> + * Returns the accumulated number of milliseconds this + * thread has been in the + * {@link java.lang.Thread.State#WAITING} or + * {@link java.lang.Thread.State#TIMED_WAITING} state, + * since thread contention monitoring was last enabled. + * A thread enters one of these states when it is waiting + * due to a call to {@link java.lang.Object.wait()}, + * {@link java.lang.Object.join()} or + * {@link java.lang.concurrent.locks.LockSupport.park()}, + * either with an infinite or timed delay, respectively. + * </p> + * <p> + * Use of this method requires virtual machine support + * for thread contention monitoring and for this support + * to be enabled. + * </p> + * + * @return the accumulated time (in milliseconds) that this + * thread has spent in one of the waiting states, since + * thread contention monitoring was enabled, or -1 + * if thread contention monitoring is disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see ThreadMXBean#isThreadContentionMonitoringEnabled() + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ + public long getWaitedTime() + { + if (bean == null) + bean = ManagementFactory.getThreadMXBean(); + // Will throw UnsupportedOperationException for us + if (bean.isThreadContentionMonitoringEnabled()) + return waitedTime; + else + return -1; + } + + /** + * Returns true if the thread is in a native method. This + * excludes native code which forms part of the virtual + * machine itself, or which results from Just-In-Time + * compilation. + * + * @return true if the thread is in a native method, false + * otherwise. + */ + public boolean isInNative() + { + return isInNative; + } + + /** + * Returns true if the thread has been suspended using + * {@link java.lang.Thread#suspend()}. + * + * @return true if the thread is suspended, false otherwise. + */ + public boolean isSuspended() + { + return isSuspended; + } + + /** + * Returns a {@link java.lang.String} representation of + * this {@link ThreadInfo} object. This takes the form + * <code>java.lang.management.ThreadInfo[id=tid, name=n, + * state=s, blockedCount=bc, waitedCount=wc, isInNative=iin, + * isSuspended=is]</code>, where <code>tid</code> is + * the thread identifier, <code>n</code> is the + * thread name, <code>s</code> is the thread state, + * <code>bc</code> is the blocked state count, + * <code>wc</code> is the waiting state count and + * <code>iin</code> and <code>is</code> are boolean + * flags to indicate the thread is in native code or + * suspended respectively. If the thread is blocked, + * <code>lock=l, lockOwner=lo</code> is also included, + * where <code>l</code> is the lock waited for, and + * <code>lo</code> is the thread which owns the lock + * (or null if there is no owner). + * + * @return the string specified above. + */ + public String toString() + { + return getClass().getName() + + "[id=" + threadId + + ", name=" + threadName + + ", state=" + threadState + + ", blockedCount=" + blockedCount + + ", waitedCount=" + waitedCount + + ", isInNative=" + isInNative + + ", isSuspended=" + isSuspended + + (threadState.equals("BLOCKED") ? + ", lockOwnerId=" + lockOwnerId + + ", lockOwnerName=" + lockOwnerName : "") + + "]"; + } + +} diff --git a/libjava/classpath/java/lang/management/ThreadMXBean.java b/libjava/classpath/java/lang/management/ThreadMXBean.java new file mode 100644 index 00000000000..669cb3cd8d2 --- /dev/null +++ b/libjava/classpath/java/lang/management/ThreadMXBean.java @@ -0,0 +1,497 @@ +/* ThreadMXBean.java - Interface for a thread bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * <p> + * Provides access to information about the threads + * of the virtual machine. An instance of this bean is + * obtained by calling + * {@link ManagementFactory#getThreadMXBean()}. + * </p> + * <p> + * Each thread within the virtual machine is given an + * identifier, which is guaranteed to be unique to a + * particular thread over its lifetime (after which it + * may be reused). The identifier for a thread may be + * obtained by calling {@link java.lang.Thread#getId()}. + * This identifier is used within implementations of this + * interface to obtain information about a particular thread + * (or series of threads, in the case of an array of identifiers). + * </p> + * <p> + * This bean supports some optional behaviour, which all + * virtual machines may not choose to implement. Specifically, + * this includes the monitoring of the CPU time used by a + * thread, and the monitoring of thread contention. The former + * is further subdivided into the monitoring of either just + * the current thread or all threads. The methods + * {@link #isThreadCpuTimeSupported()}, + * {@link #isCurrentThreadCpuTimeSupported()} and + * {@link #isThreadContentionMonitoringSupported()} may be + * used to determine whether or not this functionality is + * supported. + * </p> + * <p> + * Furthermore, both these facilities may be disabled. + * In fact, thread contention monitoring is disabled by + * default, and must be explictly turned on by calling + * the {@link #setThreadContentionMonitoringEnabled(boolean)} + * method. + * </p> + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface ThreadMXBean +{ + + /** + * <p> + * This method obtains a list of threads which are deadlocked + * waiting to obtain monitor ownership. On entering a synchronized + * method of an object, or re-entering it after returning from an + * {@link java.lang.Object#wait()} call, a thread obtains ownership + * of the object's monitor. + * </p> + * <p> + * Deadlocks can occur in this situation if one or more threads end up + * waiting for a monitor, P, while also retaining ownership of a monitor, + * Q, required by the thread that currently owns P. To give a simple + * example, imagine thread A calls a synchronized method, R, obtaining the + * monitor, P. It then sleeps within that method, allowing thread B + * to run, but still retaining ownership of P. B calls another + * synchronized method, S, which causes it to obtain the monitor, Q, + * of a different object. While in that method, it then wants to + * call the original synchronized method, R, called by A. Doing so + * requires ownership of P, which is still held by A. Hence, it + * becomes blocked. + * </p> + * <p> + * A then finishes its sleep, becomes runnable, and is then allowed + * to run, being the only eligible thread in this scenario. A tries + * to call the synchronized method, S. It also gets blocked, because + * B still holds the monitor, Q. Hence, the two threads, A and B, + * are deadlocked, as neither can give up its monitor without first + * obtaining the monitor held by the other thread. + * </p> + * <p> + * Calling this method in this scenario would return the thread IDs + * of A and B. Note that this method is not designed for controlling + * synchronization, but for troubleshooting problems which cause such + * deadlocks; it may be prohibitively expensive to use in normal + * operation. + * </p> + * + * @return an array of thread identifiers, corresponding to threads + * which are currently in a deadlocked situation. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + long[] findMonitorDeadlockedThreads(); + + /** + * Returns all live thread identifiers at the time of initial + * execution. Some thread identifiers in the returned array + * may refer to terminated threads, if this occurs during the + * lifetime of this method. + * + * @return an array of thread identifiers, corresponding to + * current live threads. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + long[] getAllThreadIds(); + + /** + * <p> + * Returns the total number of nanoseconds of CPU time + * the current thread has used. This is equivalent to calling + * <code>{@link #getThreadCpuTime()}(Thread.currentThread.getId())</code>. + * </p> + * <p> + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + * </p> + * + * @return the total number of nanoseconds of CPU time the current + * thread has used, or -1 if CPU time monitoring is disabled. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getCurrentThreadUserTime() + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getCurrentThreadCpuTime(); + + /** + * <p> + * Returns the total number of nanoseconds of CPU time + * the current thread has executed in user mode. This is + * equivalent to calling + * <code>{@link #getThreadUserTime()}(Thread.currentThread.getId())</code>. + * </p> + * <p> + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + * </p> + * + * @return the total number of nanoseconds of CPU time the current + * thread has executed in user mode, or -1 if CPU time + * monitoring is disabled. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getCurrentThreadCpuTime() + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getCurrentThreadUserTime(); + + /** + * Returns the number of live daemon threads. + * + * @return the number of live daemon threads. + */ + int getDaemonThreadCount(); + + /** + * Returns the peak number of live threads since + * the virtual machine was started or the count + * reset using {@link #resetPeakThreadCount()}. + * + * @return the peak live thread count. + * @see #resetPeakThreadCount() + */ + int getPeakThreadCount(); + + /** + * Returns the number of live threads, including + * both daemon threads and non-daemon threads. + * + * @return the current number of live threads. + */ + int getThreadCount(); + + /** + * <p> + * Returns the total number of nanoseconds of CPU time + * the specified thread has used. + * </p> + * <p> + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + * </p> + * + * @param id the thread identifier of the thread whose CPU time is being + * monitored. + * @return the total number of nanoseconds of CPU time the specified + * thread has used, or -1 if CPU time monitoring is disabled. + * @throws IllegalArgumentException if <code>id</code> <= 0. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getThreadUserTime(long) + * @see #isThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getThreadCpuTime(long id); + + /** + * Returns information on the specified thread without any + * stack trace information. This is equivalent to + * <code>{@link #getThreadInfo}(id, 0)</code>. If the + * identifier specifies a thread which is either non-existant + * or not alive, then the method returns <code>null</code>. + * + * @param id the identifier of the thread to return information + * on. + * @return a {@link ThreadInfo} object pertaining to the specified + * thread, or <code>null</code> if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if <code>id</code> <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo getThreadInfo(long id); + + /** + * Returns information on the specified threads without any + * stack trace information. This is equivalent to + * <code>{@link #getThreadInfo}(ids, 0)</code>. If an + * identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is <code>null</code>. + * + * @param ids an array of thread identifiers to return information + * on. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * <code>null</code> if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo[] getThreadInfo(long[] ids); + + /** + * Returns information on the specified thread with + * stack trace information to the supplied depth. If the + * identifier specifies a thread which is either non-existant + * or not alive, then the method returns <code>null</code>. + * A maximum depth of 0 corresponds to an empty stack trace + * (an empty array is returned by the appropriate + * {@link ThreadInfo} method). A maximum depth of + * <code>Integer.MAX_VALUE</code> returns the full stack trace. + * + * @param id the identifier of the thread to return information + * on. + * @param maxDepth the maximum depth of the stack trace. + * Values of 0 or <code>Integer.MAX_VALUE</code> + * correspond to an empty and full stack trace + * respectively. + * @return a {@link ThreadInfo} object pertaining to the specified + * thread, or <code>null</code> if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if <code>id</code> <= 0. + * @throws IllegalArgumentException if <code>maxDepth</code> < 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo getThreadInfo(long id, int maxDepth); + + /** + * Returns information on the specified threads with + * stack trace information to the supplied depth. If an + * identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is <code>null</code>. A maximum depth of 0 corresponds + * to an empty stack trace (an empty array is returned by the + * appropriate {@link ThreadInfo} method). A maximum depth of + * <code>Integer.MAX_VALUE</code> returns the full stack trace. + * + * @param ids an array of thread identifiers to return information + * on. + * @param maxDepth the maximum depth of the stack trace. + * Values of 0 or <code>Integer.MAX_VALUE</code> + * correspond to an empty and full stack trace + * respectively. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * <code>null</code> if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws IllegalArgumentException if <code>maxDepth</code> < 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo[] getThreadInfo(long[] ids, int maxDepth); + + /** + * <p> + * Returns the total number of nanoseconds of CPU time + * the specified thread has executed in user mode. + * </p> + * <p> + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + * </p> + * + * @param id the thread identifier of the thread whose CPU time is being + * monitored. + * @return the total number of nanoseconds of CPU time the specified + * thread has executed in user mode, or -1 if CPU time monitoring + * is disabled. + * @throws IllegalArgumentException if <code>id</code> <= 0. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getThreadCpuTime(long) + * @see #isThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getThreadUserTime(long id); + + /** + * Returns the total number of threads that have been + * created and started during the lifetime of the virtual + * machine. + * + * @return the total number of started threads. + */ + long getTotalStartedThreadCount(); + + /** + * Returns true if the virtual machine supports the monitoring + * of the CPU time used by the current thread. This is implied + * by {@link isThreadCpuTimeSupported()} returning true. + * + * @return true if monitoring of the CPU time used by the current + * thread is supported by the virtual machine. + * @see #isThreadCpuTimeEnabled() + * @see #isThreadCpuTimeSupported() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isCurrentThreadCpuTimeSupported(); + + /** + * Returns true if thread contention monitoring is currently + * enabled. + * + * @return true if thread contention monitoring is enabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see #isThreadContentionMonitoringSupported() + * @see #setThreadContentionMonitoringEnabled(boolean) + */ + boolean isThreadContentionMonitoringEnabled(); + + /** + * Returns true if thread contention monitoring is supported + * by the virtual machine. + * + * @return true if thread contention monitoring is supported + * by the virtual machine. + * @see #isThreadContentionMonitoringEnabled() + * @see #setThreadContentionMonitoringEnabled(boolean) + */ + boolean isThreadContentionMonitoringSupported(); + + /** + * Returns true if monitoring of the CPU time used by a thread + * is currently enabled. + * + * @return true if thread CPU time monitoring is enabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support CPU time + * monitoring. + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeSupported() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isThreadCpuTimeEnabled(); + + /** + * Returns true if the virtual machine supports the monitoring + * of the CPU time used by all threads. This implies + * that {@link isCurrentThreadCpuTimeSupported()} returns true. + * + * @return true if monitoring of the CPU time used by the current + * thread is supported by the virtual machine. + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isThreadCpuTimeSupported(); + + /** + * Resets the peak live thread count to the + * current number of live threads, as returned + * by {@link #getThreadCount()}. + * + * @see #getPeakThreadCount() + * @see #getThreadCount() + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void resetPeakThreadCount(); + + /** + * Toggles the monitoring of thread contention. Thread + * contention monitoring is disabled by default. Each + * time contention monitoring is re-enabled, the times + * it maintains are reset. + * + * @param enable true if monitoring should be enabled, + * false if it should be disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #isThreadContentionMonitoringEnabled() + * @see #isThreadContentionMonitoringSupported() + */ + void setThreadContentionMonitoringEnabled(boolean enable); + + /** + * Toggles the monitoring of CPU time used by threads. The + * initial setting is dependent on the underlying virtual + * machine. On enabling CPU time monitoring, the virtual + * machine may take any value up to and including the current + * time as the start time for monitoring. + * + * @param enable true if monitoring should be enabled, + * false if it should be disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support CPU time + * monitoring. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #isThreadCpuTimeSupported() + */ + void setThreadCpuTimeEnabled(boolean enable); + +} diff --git a/libjava/classpath/java/lang/management/package.html b/libjava/classpath/java/lang/management/package.html new file mode 100644 index 00000000000..1b37cc1a529 --- /dev/null +++ b/libjava/classpath/java/lang/management/package.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in java.lang.management package. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - java.lang.management</title></head> + +<body> + +<p> +A series of management beans which provide access to information about the +virtual machine and its underlying operating system. +</p> +<p>The following beans are provided:</p> +<ul> +<li> +<span style="font-weight: bold;">{@link java.lang.management.OperatingSystemMXBean} </span> +— Information about the underlying operating system. +</li> +</ul> +<h2>Accessing the Beans</h2> +<p> +An instance of a bean can be obtained by using one of the following methods: +</p> +<ol> +<li>Calling the appropriate static method of the {@link java.lang.management.ManagementFactory} +</li> +</ol> +</body> +</html> diff --git a/libjava/classpath/java/math/BigDecimal.java b/libjava/classpath/java/math/BigDecimal.java index 94b373b04be..bca9b12c28b 100644 --- a/libjava/classpath/java/math/BigDecimal.java +++ b/libjava/classpath/java/math/BigDecimal.java @@ -1,5 +1,5 @@ /* java.math.BigDecimal -- Arbitrary precision decimals. - Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,7 @@ public class BigDecimal extends Number implements Comparable { private BigInteger intVal; private int scale; + private int precision = 0; private static final long serialVersionUID = 6108874887143696463L; /** @@ -48,21 +49,21 @@ public class BigDecimal extends Number implements Comparable * @since 1.5 */ public static final BigDecimal ZERO = - new BigDecimal (BigInteger.valueOf (0), 0); + new BigDecimal (BigInteger.ZERO, 0); /** * The constant one as a BigDecimal with scale zero. * @since 1.5 */ public static final BigDecimal ONE = - new BigDecimal (BigInteger.valueOf (1), 0); + new BigDecimal (BigInteger.ONE, 0); /** * The constant ten as a BigDecimal with scale zero. * @since 1.5 */ public static final BigDecimal TEN = - new BigDecimal (BigInteger.valueOf (10), 0); + new BigDecimal (BigInteger.TEN, 0); public static final int ROUND_UP = 0; public static final int ROUND_DOWN = 1; @@ -73,19 +74,181 @@ public class BigDecimal extends Number implements Comparable public static final int ROUND_HALF_EVEN = 6; public static final int ROUND_UNNECESSARY = 7; + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + * @since 1.5 + */ + public BigDecimal (int val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal using the BigDecimal(int) constructor and then + * rounds according to the MathContext. + * @param val the value for the initial (unrounded) BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (int val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + */ + public BigDecimal (long val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal from the long in the same way as BigDecimal(long) + * and then rounds according to the MathContext. + * @param val the long from which we create the initial BigDecimal + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (long val, MathContext mc) + { + this(val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose value is given by num rounded according to + * mc. Since num is already a BigInteger, the rounding refers only to the + * precision setting in mc, if mc.getPrecision() returns an int lower than + * the number of digits in num, then rounding is necessary. + * @param num the unscaledValue, before rounding + * @param mc the MathContext that specifies the precision + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * * @since 1.5 + */ + public BigDecimal (BigInteger num, MathContext mc) + { + this (num, 0); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal from the String val according to the same + * rules as the BigDecimal(String) constructor and then rounds + * according to the MathContext mc. + * @param val the String from which we construct the initial BigDecimal + * @param mc the MathContext that specifies the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (String val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is zero. + * @param num the value of the new BigDecimal + */ public BigDecimal (BigInteger num) { this (num, 0); } - public BigDecimal (BigInteger num, int scale) throws NumberFormatException + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is scale. + * @param num + * @param scale + */ + public BigDecimal (BigInteger num, int scale) { - if (scale < 0) - throw new NumberFormatException ("scale of " + scale + " is < 0"); this.intVal = num; this.scale = scale; } + + /** + * Constructs a BigDecimal using the BigDecimal(BigInteger, int) + * constructor and then rounds according to the MathContext. + * @param num the unscaled value of the unrounded BigDecimal + * @param scale the scale of the unrounded BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (BigInteger num, int scale, MathContext mc) + { + this (num, scale); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + /** + * Constructs a BigDecimal in the same way as BigDecimal(double) and then + * rounds according to the MathContext. + * @param num the double from which the initial BigDecimal is created + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (double num, MathContext mc) + { + this (num); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + public BigDecimal (double num) throws NumberFormatException { if (Double.isInfinite (num) || Double.isNaN (num)) @@ -103,8 +266,10 @@ public class BigDecimal extends Number implements Comparable long mantissa = bits & mantMask; long exponent = (bits >>> mantissaBits) & expMask; boolean denormal = exponent == 0; + // Correct the exponent for the bias. exponent -= denormal ? 1022 : 1023; + // Now correct the exponent to account for the bits to the right // of the decimal. exponent -= mantissaBits; @@ -136,6 +301,209 @@ public class BigDecimal extends Number implements Comparable } } + /** + * Constructs a BigDecimal from the char subarray and rounding + * according to the MathContext. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @param mc the MathContext for rounding + * @throws NumberFormatException if the char subarray is not a valid + * BigDecimal representation + * @throws ArithmeticException if the result is inexact but the rounding + * mode is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len, MathContext mc) + { + this(in, offset, len); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the char array and rounding according + * to the MathContext. + * @param in the char array + * @param mc the MathContext + * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal + * representation + * @throws ArithmeticException if the result is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, MathContext mc) + { + this(in, 0, in.length); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the given char array, accepting the same + * sequence of characters as the BigDecimal(String) constructor. + * @param in the char array + * @throws NumberFormatException if <code>in</code> is not a valid BigDecimal + * representation + * @since 1.5 + */ + public BigDecimal(char[] in) + { + this(in, 0, in.length); + } + + /** + * Constructs a BigDecimal from a char subarray, accepting the same sequence + * of characters as the BigDecimal(String) constructor. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @throws NumberFormatException if <code>in</code> is not a valid + * BigDecimal representation. + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len) + { + // start is the index into the char array where the significand starts + int start = offset; + // end is one greater than the index of the last character used + int end = offset + len; + // point is the index into the char array where the exponent starts + // (or, if there is no exponent, this is equal to end) + int point = offset; + // dot is the index into the char array where the decimal point is + // found, or -1 if there is no decimal point + int dot = -1; + + // The following examples show what these variables mean. Note that + // point and dot don't yet have the correct values, they will be + // properly assigned in a loop later on in this method. + // + // Example 1 + // + // + 1 0 2 . 4 6 9 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = 6, point = end = 10 + // + // Example 2 + // + // + 2 3 4 . 6 1 3 E - 1 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 11, start = 3, dot = 6, point = 10, end = 13 + // + // Example 3 + // + // - 1 2 3 4 5 e 7 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = -1, point = 8, end = 10 + + // Determine the sign of the number. + boolean negative = false; + if (in[offset] == '+') + { + ++start; + ++point; + } + else if (in[offset] == '-') + { + ++start; + ++point; + negative = true; + } + + // Check each character looking for the decimal point and the + // start of the exponent. + while (point < end) + { + char c = in[point]; + if (c == '.') + { + // If dot != -1 then we've seen more than one decimal point. + if (dot != -1) + throw new NumberFormatException("multiple `.'s in number"); + dot = point; + } + // Break when we reach the start of the exponent. + else if (c == 'e' || c == 'E') + break; + // Throw an exception if the character was not a decimal or an + // exponent and is not a digit. + else if (!Character.isDigit(c)) + throw new NumberFormatException("unrecognized character at " + point + + ": " + c); + ++point; + } + + // val is a StringBuilder from which we'll create a BigInteger + // which will be the unscaled value for this BigDecimal + StringBuilder val = new StringBuilder(point - start - 1); + if (dot != -1) + { + // If there was a decimal we must combine the two parts that + // contain only digits and we must set the scale properly. + val.append(in, start, dot - start); + val.append(in, dot + 1, point - dot - 1); + scale = point - 1 - dot; + } + else + { + // If there was no decimal then the unscaled value is just the number + // formed from all the digits and the scale is zero. + val.append(in, start, point - start); + scale = 0; + } + if (val.length() == 0) + throw new NumberFormatException("no digits seen"); + + // Prepend a negative sign if necessary. + if (negative) + val.insert(0, '-'); + intVal = new BigInteger(val.toString()); + + // Now parse exponent. + // If point < end that means we broke out of the previous loop when we + // saw an 'e' or an 'E'. + if (point < end) + { + point++; + // Ignore a '+' sign. + if (in[point] == '+') + point++; + + // Throw an exception if there were no digits found after the 'e' + // or 'E'. + if (point >= end) + throw new NumberFormatException("no exponent following e or E"); + + try + { + // Adjust the scale according to the exponent. + // Remember that the value of a BigDecimal is + // unscaledValue x Math.pow(10, -scale) + scale -= Integer.parseInt(new String(in, point, end - point)); + } + catch (NumberFormatException ex) + { + throw new NumberFormatException("malformed exponent"); + } + } + } + public BigDecimal (String num) throws NumberFormatException { int len = num.length(); @@ -199,18 +567,8 @@ public class BigDecimal extends Number implements Comparable throw new NumberFormatException ("no exponent following e or E"); try - { - int exp = Integer.parseInt (num.substring (point)); - exp -= scale; - if (signum () == 0) - scale = 0; - else if (exp > 0) - { - intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp)); - scale = 0; - } - else - scale = - exp; + { + scale -= Integer.parseInt (num.substring (point)); } catch (NumberFormatException ex) { @@ -247,29 +605,74 @@ public class BigDecimal extends Number implements Comparable BigInteger op1 = intVal; BigInteger op2 = val.intVal; if (scale < val.scale) - op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale)); + op1 = op1.multiply (BigInteger.TEN.pow (val.scale - scale)); else if (scale > val.scale) - op2 = op2.multiply (BigInteger.valueOf (10).pow (scale - val.scale)); + op2 = op2.multiply (BigInteger.TEN.pow (scale - val.scale)); return new BigDecimal (op1.add (op2), Math.max (scale, val.scale)); } + + /** + * Returns a BigDecimal whose value is found first by calling the + * method add(val) and then by rounding according to the MathContext mc. + * @param val the augend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return <code>this</code> + <code>val</code>, rounded if need be + * @since 1.5 + */ + public BigDecimal add (BigDecimal val, MathContext mc) + { + return add(val).round(mc); + } public BigDecimal subtract (BigDecimal val) { return this.add(val.negate()); } + /** + * Returns a BigDecimal whose value is found first by calling the + * method subtract(val) and then by rounding according to the MathContext mc. + * @param val the subtrahend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return <code>this</code> - <code>val</code>, rounded if need be + * @since 1.5 + */ + public BigDecimal subtract (BigDecimal val, MathContext mc) + { + return subtract(val).round(mc); + } + public BigDecimal multiply (BigDecimal val) { return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); } + + /** + * Returns a BigDecimal whose value is (this x val) before it is rounded + * according to the MathContext mc. + * @param val the multiplicand + * @param mc the MathContext for rounding + * @return a new BigDecimal with value approximately (this x val) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal multiply (BigDecimal val, MathContext mc) + { + return multiply(val).round(mc); + } public BigDecimal divide (BigDecimal val, int roundingMode) throws ArithmeticException, IllegalArgumentException { return divide (val, scale, roundingMode); } - + public BigDecimal divide(BigDecimal val, int newScale, int roundingMode) throws ArithmeticException, IllegalArgumentException { @@ -277,9 +680,6 @@ public class BigDecimal extends Number implements Comparable throw new IllegalArgumentException("illegal rounding mode: " + roundingMode); - if (newScale < 0) - throw new ArithmeticException ("scale is negative: " + newScale); - if (intVal.signum () == 0) // handle special case of 0.0/0.0 return newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale); @@ -290,11 +690,11 @@ public class BigDecimal extends Number implements Comparable { // Effectively increase the scale of val to avoid an // ArithmeticException for a negative power. - valIntVal = valIntVal.multiply (BigInteger.valueOf (10).pow (-power)); + valIntVal = valIntVal.multiply (BigInteger.TEN.pow (-power)); power = 0; } - BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (power)); + BigInteger dividend = intVal.multiply (BigInteger.TEN.pow (power)); BigInteger parts[] = dividend.divideAndRemainder (valIntVal); @@ -303,7 +703,7 @@ public class BigDecimal extends Number implements Comparable return new BigDecimal (unrounded, newScale); if (roundingMode == ROUND_UNNECESSARY) - throw new ArithmeticException ("newScale is not large enough"); + throw new ArithmeticException ("Rounding necessary"); int sign = intVal.signum () * valIntVal.signum (); @@ -348,16 +748,95 @@ public class BigDecimal extends Number implements Comparable // roundingMode == ROUND_DOWN return new BigDecimal (unrounded, newScale); } + + /** + * Performs division, if the resulting quotient requires rounding + * (has a nonterminating decimal expansion), + * an ArithmeticException is thrown. + * #see divide(BigDecimal, int, int) + * @since 1.5 + */ + public BigDecimal divide(BigDecimal divisor) + throws ArithmeticException, IllegalArgumentException + { + return divide(divisor, scale, ROUND_UNNECESSARY); + } + + /** + * Returns a BigDecimal whose value is the remainder in the quotient + * this / val. This is obtained by + * subtract(divideToIntegralValue(val).multiply(val)). + * @param val the divisor + * @return a BigDecimal whose value is the remainder + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal remainder(BigDecimal val) + { + return subtract(divideToIntegralValue(val).multiply(val)); + } + + /** + * Returns a BigDecimal array, the first element of which is the integer part + * of this / val, and the second element of which is the remainder of + * that quotient. + * @param val the divisor + * @return the above described BigDecimal array + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal[] divideAndRemainder(BigDecimal val) + { + BigDecimal[] result = new BigDecimal[2]; + result[0] = divideToIntegralValue(val); + result[1] = subtract(result[0].multiply(val)); + return result; + } + + /** + * Returns a BigDecimal whose value is the integer part of the quotient + * this / val. The preferred scale is this.scale - val.scale. + * @param val the divisor + * @return a BigDecimal whose value is the integer part of this / val. + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal divideToIntegralValue(BigDecimal val) + { + return divide(val, ROUND_DOWN).floor().setScale(scale - val.scale, ROUND_DOWN); + } + + /** + * Mutates this BigDecimal into one with no fractional part, whose value is + * equal to the largest integer that is <= to this BigDecimal. Note that + * since this method is private it is okay to mutate this BigDecimal. + * @return the BigDecimal obtained through the floor operation on this + * BigDecimal. + */ + private BigDecimal floor() + { + if (scale <= 0) + return this; + String intValStr = intVal.toString(); + intValStr = intValStr.substring(0, intValStr.length() - scale); + intVal = new BigInteger(intValStr).multiply(BigInteger.TEN.pow(scale)); + return this; + } - public int compareTo (BigDecimal val) + public int compareTo (Object obj) + { + return compareTo((BigDecimal) obj); + } + + public int compareTo (BigDecimal val) { if (scale == val.scale) return intVal.compareTo (val.intVal); BigInteger thisParts[] = - intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale)); + intVal.divideAndRemainder (BigInteger.TEN.pow (scale)); BigInteger valParts[] = - val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale)); + val.intVal.divideAndRemainder (BigInteger.TEN.pow (val.scale)); int compare; if ((compare = thisParts[0].compareTo (valParts[0])) != 0) @@ -377,11 +856,6 @@ public class BigDecimal extends Number implements Comparable return thisParts[1].compareTo (valParts[1]); } - public int compareTo (Object val) - { - return(compareTo((BigDecimal)val)); - } - public boolean equals (Object o) { return (o instanceof BigDecimal @@ -430,7 +904,7 @@ public class BigDecimal extends Number implements Comparable return new BigDecimal (intVal, scale - n); return new BigDecimal (intVal.multiply - (BigInteger.valueOf (10).pow (n - scale)), 0); + (BigInteger.TEN.pow (n - scale)), 0); } public int signum () @@ -457,21 +931,296 @@ public class BigDecimal extends Number implements Comparable { return new BigDecimal (intVal.negate (), scale); } + + /** + * Returns a BigDecimal whose value is found first by negating this via + * the negate() method, then by rounding according to the MathContext mc. + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is approximately (-this) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal negate(MathContext mc) + { + BigDecimal result = negate(); + if (mc.getPrecision() != 0) + result = result.round(mc); + return result; + } + + /** + * Returns this BigDecimal. This is included for symmetry with the + * method negate(). + * @return this + * @since 1.5 + */ + public BigDecimal plus() + { + return this; + } + + /** + * Returns a BigDecimal whose value is found by rounding <code>this</code> + * according to the MathContext. This is the same as round(MathContext). + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is <code>this</code> before being rounded + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal plus(MathContext mc) + { + return round(mc); + } + + /** + * Returns a BigDecimal which is this BigDecimal rounded according to the + * MathContext rounding settings. + * @param mc the MathContext that tells us how to round + * @return the rounded BigDecimal + */ + public BigDecimal round(MathContext mc) + { + int mcPrecision = mc.getPrecision(); + int numToChop = precision() - mcPrecision; + // If mc specifies not to chop any digits or if we've already chopped + // enough digits (say by using a MathContext in the constructor for this + // BigDecimal) then just return this. + if (mcPrecision == 0 || numToChop <= 0) + return this; + + // Make a new BigDecimal which is the correct power of 10 to chop off + // the required number of digits and then call divide. + BigDecimal div = new BigDecimal(BigInteger.TEN.pow(numToChop)); + BigDecimal rounded = divide(div, scale, 4); + rounded.scale -= numToChop; + rounded.precision = mcPrecision; + return rounded; + } + + /** + * Returns the precision of this BigDecimal (the number of digits in the + * unscaled value). The precision of a zero value is 1. + * @return the number of digits in the unscaled value, or 1 if the value + * is zero. + */ + public int precision() + { + if (precision == 0) + { + String s = intVal.toString(); + precision = s.length() - (( s.charAt(0) == '-' ) ? 1 : 0); + } + return precision; + } + + /** + * Returns the String representation of this BigDecimal, using scientific + * notation if necessary. The following steps are taken to generate + * the result: + * + * 1. the BigInteger unscaledValue's toString method is called and if + * <code>scale == 0<code> is returned. + * 2. an <code>int adjExp</code> is created which is equal to the negation + * of <code>scale</code> plus the number of digits in the unscaled value, + * minus one. + * 3. if <code>scale >= 0 && adjExp >= -6</code> then we represent this + * BigDecimal without scientific notation. A decimal is added if the + * scale is positive and zeros are prepended as necessary. + * 4. if scale is negative or adjExp is less than -6 we use scientific + * notation. If the unscaled value has more than one digit, a decimal + * as inserted after the first digit, the character 'E' is appended + * and adjExp is appended. + */ + public String toString() + { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + + StringBuilder val = new StringBuilder(); + + if (scale >= 0 && (point - 1) >= -6) + { + // Convert to character form without scientific notation. + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + val.append(bigStr); + // If there is more than one digit in the unscaled value we put a + // decimal after the first digit. + if (bigStr.length() > 1) + val.insert( ( negative ? 2 : 1 ), '.'); + // And then append 'E' and the exponent = (point - 1). + val.append('E'); + if (point - 1 >= 0) + val.append('+'); + val.append( point - 1 ); + } + return val.toString(); + } + + /** + * Returns the String representation of this BigDecimal, using engineering + * notation if necessary. This is similar to toString() but when exponents + * are used the exponent is made to be a multiple of 3 such that the integer + * part is between 1 and 999. + * + * @return a String representation of this BigDecimal in engineering notation + * @since 1.5 + */ + public String toEngineeringString() + { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; - public String toString () + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + + // This is the adjusted exponent described above. + int adjExp = point - 1; + StringBuilder val = new StringBuilder(); + + if (scale >= 0 && adjExp >= -6) + { + // Convert to character form without scientific notation. + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + // The exponent must be a multiple of 3 and the integer part + // must be between 1 and 999. + val.append(bigStr); + int zeros = adjExp % 3; + int dot = 1; + if (adjExp > 0) + { + // If the exponent is positive we just move the decimal to the + // right and decrease the exponent until it is a multiple of 3. + dot += zeros; + adjExp -= zeros; + } + else + { + // If the exponent is negative then we move the dot to the right + // and decrease the exponent (increase its magnitude) until + // it is a multiple of 3. Note that this is not adjExp -= zeros + // because the mod operator doesn't give us the distance to the + // correct multiple of 3. (-5 mod 3) is -2 but the distance from + // -5 to the correct multiple of 3 (-6) is 1, not 2. + if (zeros == -2) + { + dot += 1; + adjExp -= 1; + } + else if (zeros == -1) + { + dot += 2; + adjExp -= 2; + } + } + + // Either we have to append zeros because, for example, 1.1E+5 should + // be 110E+3, or we just have to put the decimal in the right place. + if (dot > val.length()) + { + while (dot > val.length()) + val.append('0'); + } + else if (bigStr.length() > dot) + val.insert(dot + (negative ? 1 : 0), '.'); + + // And then append 'E' and the exponent (adjExp). + val.append('E'); + if (adjExp >= 0) + val.append('+'); + val.append(adjExp); + } + return val.toString(); + } + + /** + * Returns a String representation of this BigDecimal without using + * scientific notation. This is how toString() worked for releases 1.4 + * and previous. Zeros may be added to the end of the String. For + * example, an unscaled value of 1234 and a scale of -3 would result in + * the String 1234000, but the toString() method would return + * 1.234E+6. + * @return a String representation of this BigDecimal + * @since 1.5 + */ + public String toPlainString() { + // If the scale is zero we simply return the String representation of the + // unscaled value. String bigStr = intVal.toString(); - if (scale == 0) + if (scale == 0) return bigStr; + // Remember if we have to put a negative sign at the start. boolean negative = (bigStr.charAt(0) == '-'); int point = bigStr.length() - scale - (negative ? 1 : 0); - StringBuffer sb = new StringBuffer(bigStr.length() + 2 + - (point <= 0 ? (-point + 1) : 0)); + StringBuffer sb = new StringBuffer(bigStr.length() + 2 + + (point <= 0 ? (-point + 1) : 0)); if (point <= 0) { + // We have to prepend zeros and a decimal point. if (negative) sb.append('-'); sb.append('0').append('.'); @@ -482,24 +1231,95 @@ public class BigDecimal extends Number implements Comparable } sb.append(bigStr.substring(negative ? 1 : 0)); } + else if (point < bigStr.length()) + { + // No zeros need to be prepended or appended, just put the decimal + // in the right place. + sb.append(bigStr); + sb.insert(point + (negative ? 1 : 0), '.'); + } else { - sb.append(bigStr); - sb.insert(point + (negative ? 1 : 0), '.'); + // We must append zeros instead of using scientific notation. + sb.append(bigStr); + for (int i = bigStr.length(); i < point; i++) + sb.append('0'); } return sb.toString(); } - + + /** + * Converts this BigDecimal to a BigInteger. Any fractional part will + * be discarded. + * @return a BigDecimal whose value is equal to floor[this] + */ public BigInteger toBigInteger () { - return scale == 0 ? intVal : - intVal.divide (BigInteger.valueOf (10).pow (scale)); + // If scale > 0 then we must divide, if scale > 0 then we must multiply, + // and if scale is zero then we just return intVal; + if (scale > 0) + return intVal.divide (BigInteger.TEN.pow (scale)); + else if (scale < 0) + return intVal.multiply(BigInteger.TEN.pow(-scale)); + return intVal; + } + + /** + * Converts this BigDecimal into a BigInteger, throwing an + * ArithmeticException if the conversion is not exact. + * @return a BigInteger whose value is equal to the value of this BigDecimal + * @since 1.5 + */ + public BigInteger toBigIntegerExact() + { + if (scale > 0) + { + // If we have to divide, we must check if the result is exact. + BigInteger[] result = + intVal.divideAndRemainder(BigInteger.TEN.pow(scale)); + if (result[1].equals(BigInteger.ZERO)) + return result[0]; + throw new ArithmeticException("No exact BigInteger representation"); + } + else if (scale < 0) + // If we're multiplying instead, then we needn't check for exactness. + return intVal.multiply(BigInteger.TEN.pow(-scale)); + // If the scale is zero we can simply return intVal. + return intVal; } public int intValue () { return toBigInteger ().intValue (); } + + /** + * Returns a BigDecimal which is numerically equal to this BigDecimal but + * with no trailing zeros in the representation. For example, if this + * BigDecimal has [unscaledValue, scale] = [6313000, 4] this method returns + * a BigDecimal with [unscaledValue, scale] = [6313, 1]. As another + * example, [12400, -2] would become [124, -4]. + * @return a numerically equal BigDecimal with no trailing zeros + */ + public BigDecimal stripTrailingZeros() + { + String intValStr = intVal.toString(); + int newScale = scale; + int pointer = intValStr.length() - 1; + // This loop adjusts pointer which will be used to give us the substring + // of intValStr to use in our new BigDecimal, and also accordingly + // adjusts the scale of our new BigDecimal. + while (intValStr.charAt(pointer) == '0') + { + pointer --; + newScale --; + } + // Create a new BigDecimal with the appropriate substring and then + // set its scale. + BigDecimal result = new BigDecimal(intValStr.substring(0, pointer + 1)); + result.scale = newScale; + return result; + } public long longValue () { @@ -524,6 +1344,171 @@ public class BigDecimal extends Number implements Comparable public BigDecimal setScale (int scale, int roundingMode) throws ArithmeticException, IllegalArgumentException { + // NOTE: The 1.5 JRE doesn't throw this, ones prior to it do and + // the spec says it should. Nevertheless, if 1.6 doesn't fix this + // we should consider removing it. + if( scale < 0 ) throw new ArithmeticException("Scale parameter < 0."); return divide (ONE, scale, roundingMode); } + + /** + * Returns a new BigDecimal constructed from the BigDecimal(String) + * constructor using the Double.toString(double) method to obtain + * the String. + * @param val the double value used in Double.toString(double) + * @return a BigDecimal representation of val + * @throws NumberFormatException if val is NaN or infinite + * @since 1.5 + */ + public static BigDecimal valueOf(double val) + { + if (Double.isInfinite(val) || Double.isNaN(val)) + throw new NumberFormatException("argument cannot be NaN or infinite."); + return new BigDecimal(Double.toString(val)); + } + + /** + * Returns a BigDecimal whose numerical value is the numerical value + * of this BigDecimal multiplied by 10 to the power of <code>n</code>. + * @param n the power of ten + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal scaleByPowerOfTen(int n) + { + BigDecimal result = new BigDecimal(intVal, scale - n); + result.precision = precision; + return result; + } + + /** + * Returns a BigDecimal whose value is <code>this</code> to the power of + * <code>n</code>. + * @param n the power + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal pow(int n) + { + if (n < 0 || n > 999999999) + throw new ArithmeticException("n must be between 0 and 999999999"); + BigDecimal result = new BigDecimal(intVal.pow(n), scale * n); + return result; + } + + /** + * Returns a BigDecimal whose value is determined by first calling pow(n) + * and then by rounding according to the MathContext mc. + * @param n the power + * @param mc the MathContext + * @return the new BigDecimal + * @throws ArithmeticException if n < 0 or n > 999999999 or if the result is + * inexact but the rounding is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal pow(int n, MathContext mc) + { + // FIXME: The specs claim to use the X3.274-1996 algorithm. We + // currently do not. + return pow(n).round(mc); + } + + /** + * Returns a BigDecimal whose value is the absolute value of this BigDecimal + * with rounding according to the given MathContext. + * @param mc the MathContext + * @return the new BigDecimal + */ + public BigDecimal abs(MathContext mc) + { + BigDecimal result = abs(); + result = result.round(mc); + return result; + } + + /** + * Returns the size of a unit in the last place of this BigDecimal. This + * returns a BigDecimal with [unscaledValue, scale] = [1, this.scale()]. + * @return the size of a unit in the last place of <code>this</code>. + * @since 1.5 + */ + public BigDecimal ulp() + { + return new BigDecimal(BigInteger.ONE, scale); + } + + /** + * Converts this BigDecimal to a long value. + * @return the long value + * @throws ArithmeticException if rounding occurs or if overflow occurs + * @since 1.5 + */ + public long longValueExact() + { + // Set scale will throw an exception if rounding occurs. + BigDecimal temp = setScale(0, ROUND_UNNECESSARY); + BigInteger tempVal = temp.intVal; + // Check for overflow. + long result = intVal.longValue(); + if (tempVal.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 1 + || (result < 0 && signum() == 1) || (result > 0 && signum() == -1)) + throw new ArithmeticException("this BigDecimal is too " + + "large to fit into the return type"); + + return intVal.longValue(); + } + + /** + * Converts this BigDecimal into an int by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into an <code>int</code>. + * @return an int whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into an int. + * @since 1.5 + */ + public int intValueExact() + { + long temp = longValueExact(); + int result = (int)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into an int"); + return result; + } + + /** + * Converts this BigDecimal into a byte by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into a <code>byte</code>. + * @return a byte whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a byte. + * @since 1.5 + */ + public byte byteValueExact() + { + long temp = longValueExact(); + byte result = (byte)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a byte"); + return result; + } + + /** + * Converts this BigDecimal into a short by first calling longValueExact + * and then checking that the <code>long</code> returned from that + * method fits into a <code>short</code>. + * @return a short whose value is <code>this</code> + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a short. + * @since 1.5 + */ + public short shortValueExact() + { + long temp = longValueExact(); + short result = (short)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a short"); + return result; + } } diff --git a/libjava/classpath/java/math/BigInteger.java b/libjava/classpath/java/math/BigInteger.java index 86d6924af02..b57cf607e34 100644 --- a/libjava/classpath/java/math/BigInteger.java +++ b/libjava/classpath/java/math/BigInteger.java @@ -1168,7 +1168,7 @@ public class BigInteger extends Number implements Comparable throw new ArithmeticException("non-positive modulo"); if (exponent.isNegative()) - return modInverse(m); + return modInverse(m).modPow(exponent.negate(), m); if (exponent.isOne()) return mod(m); diff --git a/libjava/classpath/java/math/MathContext.java b/libjava/classpath/java/math/MathContext.java new file mode 100644 index 00000000000..417d9c2e270 --- /dev/null +++ b/libjava/classpath/java/math/MathContext.java @@ -0,0 +1,144 @@ +/* MathContext.java -- + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.math; + +import java.io.Serializable; + +/** + * Immutable objects describing settings such as rounding mode and digit + * precision for numerical operations such as those in the BigDecimal class. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ +public final class MathContext implements Serializable +{ + + /** + * This is the serialVersionUID reported here: + * java.sun.com/j2se/1.5.0/docs/api/serialized-form.html#java.math.MathContext + */ + private static final long serialVersionUID = 5579720004786848255L; + + private int precision; + + /** + * Constructs a new MathContext with the specified precision and with HALF_UP + * rounding. + * @param setPrecision the precision for the new MathContext + * + * @throws IllegalArgumentException if precision is < 0. + */ + public MathContext(int setPrecision) + { + if (setPrecision < 0) + throw new IllegalArgumentException("Precision cannot be less than zero."); + precision = setPrecision; + } + + /** + * Constructs a MathContext from a String that has the same form as one + * produced by the toString() method. + * @param val + * + * @throws IllegalArgumentException if the String is not in the correct + * format or if the precision specified is < 0. + */ + public MathContext(String val) + { + try + { + int roundingModeIndex = val.indexOf("roundingMode", 10); + precision = Integer.parseInt(val.substring(10, roundingModeIndex - 1)); + } + catch (NumberFormatException nfe) + { + throw new IllegalArgumentException("String not in correct format"); + } + catch (IllegalArgumentException iae) + { + throw new IllegalArgumentException("String not in correct format"); + } + if (precision < 0) + throw new IllegalArgumentException("Precision cannot be less than 0."); + } + + /** + * Returns true if x is a MathContext and has the same precision setting + * and rounding mode as this MathContext. + * + * @return true if the above conditions hold + */ + public boolean equals(Object x) + { + if (!(x instanceof MathContext)) + return false; + MathContext mc = (MathContext)x; + return mc.precision == this.precision; + } + + /** + * Returns the precision setting. + * @return the precision setting. + */ + public int getPrecision() + { + return precision; + } + + /** + * Returns "precision=p roundingMode=MODE" where p is an int giving the + * precision and MODE is UP, DOWN, HALF_UP, HALF_DOWN, HALF_EVEN, CEILING, + * FLOOR, or UNNECESSARY corresponding to rounding modes. + * + * @return a String describing this MathContext + */ + public String toString() + { + return "precision="+precision; + } + + /** + * Returns the hashcode for this MathContext. + * @return the hashcode for this MathContext. + */ + public int hashCode() + { + return precision; + } +} diff --git a/libjava/classpath/java/net/Inet6Address.java b/libjava/classpath/java/net/Inet6Address.java index 0d62fe919a0..8d834a6fd28 100644 --- a/libjava/classpath/java/net/Inet6Address.java +++ b/libjava/classpath/java/net/Inet6Address.java @@ -39,13 +39,16 @@ exception statement from your version. */ package java.net; import java.util.Arrays; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; /* * Written using on-line Java Platform 1.4 API Specification and * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt) * * @author Michael Koch - * @status Believed complete and correct. + * @status Updated to 1.5. Serialization compatibility is tested. */ public final class Inet6Address extends InetAddress { @@ -57,6 +60,39 @@ public final class Inet6Address extends InetAddress byte[] ipaddress; /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private int scope_id; + + /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private boolean scope_id_set; + + /** + * Whether ifname is set or not. + * @since 1.5 + * @serial + */ + private boolean scope_ifname_set; + + /** + * Name of the network interface, used only by the serialization methods + * @since 1.5 + * @serial + */ + private String ifname; + + /** + * Scope network interface, or <code>null</code>. + */ + private transient NetworkInterface nif; + + /** * Create an Inet6Address object * * @param addr The IP address @@ -67,6 +103,10 @@ public final class Inet6Address extends InetAddress super(addr, host); // Super constructor clones the addr. Get a reference to the clone. this.ipaddress = this.addr; + ifname = null; + scope_ifname_set = scope_id_set = false; + scope_id = 0; + nif = null; } /** @@ -199,6 +239,75 @@ public final class Inet6Address extends InetAddress } /** + * Creates a scoped Inet6Address where the scope has an integer id. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + int scopeId) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.scope_id = scopeId; + ip.scope_id_set = true; + return ip; + } + + /** + * Creates a scoped Inet6Address where the scope is a given + * NetworkInterface. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + NetworkInterface nif) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.nif = nif; + + return ip; + } + + /** + * Returns the <code>NetworkInterface</code> of the address scope + * if it is a scoped address and the scope is given in the form of a + * NetworkInterface. + * (I.e. the address was created using the + * getByAddress(String, byte[], NetworkInterface) method) + * Otherwise this method returns <code>null</code>. + * @since 1.5 + */ + public NetworkInterface getScopedInterface() + { + return nif; + } + + /** + * Returns the scope ID of the address scope if it is a scoped adress using + * an integer to identify the scope. + * + * Otherwise this method returns 0. + * @since 1.5 + */ + public int getScopeId() + { + // check scope_id_set because some JDK-serialized objects seem to have + // scope_id set to a nonzero value even when scope_id_set == false + if( scope_id_set ) + return scope_id; + return 0; + } + + /** * Returns the IP address string in textual presentation */ public String getHostAddress() @@ -214,12 +323,17 @@ public final class Inet6Address extends InetAddress sbuf.append(Integer.toHexString(x)); } + if( nif != null ) + sbuf.append( "%" + nif.getName() ); + else if( scope_id_set ) + sbuf.append( "%" + scope_id ); return sbuf.toString(); } /** * Returns a hashcode for this IP address + * (The hashcode is independent of scope) */ public int hashCode() { @@ -234,10 +348,23 @@ public final class Inet6Address extends InetAddress if (! (obj instanceof Inet6Address)) return false; - // this.ipaddress is never set in this class except to - // the value of the super class' addr. The super classes - // equals(Object) will do the compare. - return super.equals(obj); + Inet6Address ip = (Inet6Address)obj; + if (ipaddress.length != ip.ipaddress.length) + return false; + + for (int i = 0; i < ip.ipaddress.length; i++) + if (ipaddress[i] != ip.ipaddress[i]) + return false; + + if( ip.nif != null && nif != null ) + return nif.equals( ip.nif ); + if( ip.nif != nif ) + return false; + if( ip.scope_id_set != scope_id_set ) + return false; + if( scope_id_set ) + return (scope_id == ip.scope_id); + return true; } /** @@ -258,4 +385,38 @@ public final class Inet6Address extends InetAddress return true; } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try + { + if( scope_ifname_set ) + nif = NetworkInterface.getByName( ifname ); + } + catch( SocketException se ) + { + // FIXME: Ignore this? or throw an IOException? + } + } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + if( nif != null ) + { + ifname = nif.getName(); + scope_ifname_set = true; + } + s.defaultWriteObject(); + } } diff --git a/libjava/classpath/java/net/InetSocketAddress.java b/libjava/classpath/java/net/InetSocketAddress.java index 91254575265..5267cc11a5d 100644 --- a/libjava/classpath/java/net/InetSocketAddress.java +++ b/libjava/classpath/java/net/InetSocketAddress.java @@ -86,7 +86,6 @@ public class InetSocketAddress extends SocketAddress this.addr = addr; this.port = port; - this.hostname = addr.getHostName(); } /** @@ -186,7 +185,7 @@ public class InetSocketAddress extends SocketAddress if (addr == null && sa.addr != null) return false; - else if (addr == null && sa.addr == null) + else if (addr == null && sa.addr == null) // we know hostname != null return hostname.equals(sa.hostname) && sa.port == port; else return addr.equals(sa.addr) && sa.port == port; @@ -213,6 +212,9 @@ public class InetSocketAddress extends SocketAddress */ public final String getHostName() { + if (hostname == null) // we know addr != null + hostname = addr.getHostName(); + return hostname; } @@ -249,10 +251,11 @@ public class InetSocketAddress extends SocketAddress /** * Returns the <code>InetSocketAddress</code> as string * - * @return A string represenation of this address. + * @return A string representation of this address. */ public String toString() { + // Note: if addr is null, then hostname != null. return (addr == null ? hostname : addr.toString()) + ":" + port; } } diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java index 967cc80f69b..ed7decc7992 100644 --- a/libjava/classpath/java/net/URL.java +++ b/libjava/classpath/java/net/URL.java @@ -482,7 +482,17 @@ public final class URL implements Serializable } catch (URLParseError e) { - throw new MalformedURLException(e.getMessage()); + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } + catch (RuntimeException e) + { + // This isn't documented, but the JDK also catches + // RuntimeExceptions here. + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; } if (hashAt >= 0) @@ -535,8 +545,7 @@ public final class URL implements Serializable */ public Object getContent(Class[] classes) throws IOException { - // FIXME: implement this - return getContent(); + return openConnection().getContent(classes); } /** diff --git a/libjava/classpath/java/net/URLClassLoader.java b/libjava/classpath/java/net/URLClassLoader.java index 183e645c85f..403f7485c79 100644 --- a/libjava/classpath/java/net/URLClassLoader.java +++ b/libjava/classpath/java/net/URLClassLoader.java @@ -39,15 +39,21 @@ exception statement from your version. */ package java.net; -import java.io.BufferedReader; +import gnu.java.net.loader.FileURLLoader; +import gnu.java.net.loader.JarURLLoader; +import gnu.java.net.loader.RemoteURLLoader; +import gnu.java.net.loader.Resource; +import gnu.java.net.loader.URLLoader; +import gnu.java.net.loader.URLStreamHandlerCache; + import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; -import java.io.FileInputStream; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSource; @@ -55,14 +61,10 @@ import java.security.PermissionCollection; import java.security.PrivilegedAction; import java.security.SecureClassLoader; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.StringTokenizer; import java.util.Vector; import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -128,19 +130,17 @@ public class URLClassLoader extends SecureClassLoader // Class Variables /** - * A global cache to store mappings between URLLoader and URL, - * so we can avoid do all the homework each time the same URL - * comes. - * XXX - Keeps these loaders forever which prevents garbage collection. + * A cache to store mappings between handler factory and its + * private protocol handler cache (also a HashMap), so we can avoid + * creating handlers each time the same protocol comes. */ - private static HashMap urlloaders = new HashMap(); + private static URLStreamHandlerCache factoryCache + = new URLStreamHandlerCache(); /** - * A cache to store mappings between handler factory and its - * private protocol handler cache (also a HashMap), so we can avoid - * create handlers each time the same protocol comes. + * The prefix for URL loaders. */ - private static HashMap factoryCache = new HashMap(5); + private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_"; // Instance variables @@ -168,516 +168,6 @@ public class URLClassLoader extends SecureClassLoader // Helper classes /** - * A <code>URLLoader</code> contains all logic to load resources from a - * given base <code>URL</code>. - */ - abstract static class URLLoader - { - /** - * Our classloader to get info from if needed. - */ - final URLClassLoader classloader; - - /** - * The base URL from which all resources are loaded. - */ - final URL baseURL; - - /** - * A <code>CodeSource</code> without any associated certificates. - * It is common for classes to not have certificates associated - * with them. If they come from the same <code>URLLoader</code> - * then it is safe to share the associated <code>CodeSource</code> - * between them since <code>CodeSource</code> is immutable. - */ - final CodeSource noCertCodeSource; - - URLLoader(URLClassLoader classloader, URL baseURL) - { - this(classloader, baseURL, baseURL); - } - - URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL) - { - this.classloader = classloader; - this.baseURL = baseURL; - this.noCertCodeSource = new CodeSource(overrideURL, null); - } - - /** - * Returns a <code>Resource</code> loaded by this - * <code>URLLoader</code>, or <code>null</code> when no - * <code>Resource</code> with the given name exists. - */ - abstract Resource getResource(String s); - - /** - * Returns the <code>Manifest</code> associated with the - * <code>Resource</code>s loaded by this <code>URLLoader</code> or - * <code>null</code> there is no such <code>Manifest</code>. - */ - Manifest getManifest() - { - return null; - } - - Vector getClassPath() - { - return null; - } - } - - /** - * A <code>Resource</code> represents a resource in some - * <code>URLLoader</code>. It also contains all information (e.g., - * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and - * <code>InputStream</code>) that is necessary for loading resources - * and creating classes from a <code>URL</code>. - */ - abstract static class Resource - { - final URLLoader loader; - - Resource(URLLoader loader) - { - this.loader = loader; - } - - /** - * Returns the non-null <code>CodeSource</code> associated with - * this resource. - */ - CodeSource getCodeSource() - { - Certificate[] certs = getCertificates(); - if (certs == null) - return loader.noCertCodeSource; - else - return new CodeSource(loader.baseURL, certs); - } - - /** - * Returns <code>Certificates</code> associated with this - * resource, or null when there are none. - */ - Certificate[] getCertificates() - { - return null; - } - - /** - * Return a <code>URL</code> that can be used to access this resource. - */ - abstract URL getURL(); - - /** - * Returns the size of this <code>Resource</code> in bytes or - * <code>-1</code> when unknown. - */ - abstract int getLength(); - - /** - * Returns the non-null <code>InputStream</code> through which - * this resource can be loaded. - */ - abstract InputStream getInputStream() throws IOException; - } - - /** - * A <code>JarURLLoader</code> is a type of <code>URLLoader</code> - * only loading from jar url. - */ - static final class JarURLLoader extends URLLoader - { - final JarFile jarfile; // The jar file for this url - final URL baseJarURL; // Base jar: url for all resources loaded from jar - - Vector classPath; // The "Class-Path" attribute of this Jar's manifest - - public JarURLLoader(URLClassLoader classloader, URL baseURL, - URL absoluteUrl) - { - super(classloader, baseURL, absoluteUrl); - - // Cache url prefix for all resources in this jar url. - String external = baseURL.toExternalForm(); - StringBuffer sb = new StringBuffer(external.length() + 6); - sb.append("jar:"); - sb.append(external); - sb.append("!/"); - String jarURL = sb.toString(); - - this.classPath = null; - URL baseJarURL = null; - JarFile jarfile = null; - try - { - baseJarURL = - new URL(null, jarURL, classloader.getURLStreamHandler("jar")); - - jarfile = - ((JarURLConnection) baseJarURL.openConnection()).getJarFile(); - - Manifest manifest; - Attributes attributes; - String classPathString; - - this.classPath = new Vector(); - - // This goes through the cached jar files listed - // in the INDEX.LIST file. All the jars found are added - // to the classPath vector so they can be loaded. - String dir = "META-INF/INDEX.LIST"; - if (jarfile.getEntry(dir) != null) - { - BufferedReader br = new BufferedReader(new InputStreamReader(new URL(baseJarURL, - dir).openStream())); - String line = br.readLine(); - while (line != null) - { - if (line.endsWith(".jar")) - { - try - { - this.classPath.add(new URL(baseURL, line)); - } - catch (java.net.MalformedURLException xx) - { - // Give up - } - } - line = br.readLine(); - } - } - else if ((manifest = jarfile.getManifest()) != null - && (attributes = manifest.getMainAttributes()) != null - && ((classPathString - = attributes.getValue(Attributes.Name.CLASS_PATH)) - != null)) - { - StringTokenizer st = new StringTokenizer(classPathString, " "); - while (st.hasMoreElements ()) - { - String e = st.nextToken (); - try - { - this.classPath.add(new URL(baseURL, e)); - } - catch (java.net.MalformedURLException xx) - { - // Give up - } - } - } - } - catch (IOException ioe) - { - /* ignored */ - } - - this.baseJarURL = baseJarURL; - this.jarfile = jarfile; - } - - /** get resource with the name "name" in the jar url */ - Resource getResource(String name) - { - if (jarfile == null) - return null; - - if (name.startsWith("/")) - name = name.substring(1); - - JarEntry je = jarfile.getJarEntry(name); - if (je != null) - return new JarURLResource(this, name, je); - else - return null; - } - - Manifest getManifest() - { - try - { - return (jarfile == null) ? null : jarfile.getManifest(); - } - catch (IOException ioe) - { - return null; - } - } - - Vector getClassPath() - { - return classPath; - } - } - - static final class JarURLResource extends Resource - { - private final JarEntry entry; - private final String name; - - JarURLResource(JarURLLoader loader, String name, JarEntry entry) - { - super(loader); - this.entry = entry; - this.name = name; - } - - InputStream getInputStream() throws IOException - { - return ((JarURLLoader) loader).jarfile.getInputStream(entry); - } - - int getLength() - { - return (int) entry.getSize(); - } - - Certificate[] getCertificates() - { - // We have to get the entry from the jar file again, because the - // certificates will not be available until the entire entry has - // been read. - return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name)) - .getCertificates(); - } - - URL getURL() - { - try - { - return new URL(((JarURLLoader) loader).baseJarURL, name, - loader.classloader.getURLStreamHandler("jar")); - } - catch (MalformedURLException e) - { - InternalError ie = new InternalError(); - ie.initCause(e); - throw ie; - } - } - } - - /** - * Loader for remote directories. - */ - static final class RemoteURLLoader extends URLLoader - { - private final String protocol; - - RemoteURLLoader(URLClassLoader classloader, URL url) - { - super(classloader, url); - protocol = url.getProtocol(); - } - - /** - * Get a remote resource. - * Returns null if no such resource exists. - */ - Resource getResource(String name) - { - try - { - URL url = - new URL(baseURL, name, classloader.getURLStreamHandler(protocol)); - URLConnection connection = url.openConnection(); - - // Open the connection and check the stream - // just to be sure it exists. - int length = connection.getContentLength(); - InputStream stream = connection.getInputStream(); - - // We can do some extra checking if it is a http request - if (connection instanceof HttpURLConnection) - { - int response = - ((HttpURLConnection) connection).getResponseCode(); - if (response / 100 != 2) - return null; - } - - if (stream != null) - return new RemoteResource(this, name, url, stream, length); - else - return null; - } - catch (IOException ioe) - { - return null; - } - } - } - - /** - * A resource from some remote location. - */ - static final class RemoteResource extends Resource - { - private final URL url; - private final InputStream stream; - private final int length; - - RemoteResource(RemoteURLLoader loader, String name, URL url, - InputStream stream, int length) - { - super(loader); - this.url = url; - this.stream = stream; - this.length = length; - } - - InputStream getInputStream() throws IOException - { - return stream; - } - - public int getLength() - { - return length; - } - - public URL getURL() - { - return url; - } - } - - /** - * A <code>FileURLLoader</code> is a type of <code>URLLoader</code> - * only loading from file url. - */ - static final class FileURLLoader extends URLLoader - { - File dir; //the file for this file url - - FileURLLoader(URLClassLoader classloader, URL url, URL absoluteUrl) - { - super(classloader, url, absoluteUrl); - dir = new File(absoluteUrl.getFile()); - } - - /** get resource with the name "name" in the file url */ - Resource getResource(String name) - { - try - { - // Make sure that all components in name are valid by walking through - // them - File file = walkPathComponents(name); - - if (file == null) - return null; - - return new FileResource(this, file); - } - catch (IOException e) - { - // Fall through... - } - return null; - } - - /** - * Walk all path tokens and check them for validity. At no moment, we are - * allowed to reach a directory located "above" the root directory, stored - * in "dir" property. We are also not allowed to enter a non existing - * directory or a non directory component (plain file, symbolic link, ...). - * An empty or null path is valid. Pathnames components are separated by - * <code>File.separatorChar</code> - * - * @param resourceFileName the name to be checked for validity. - * @return the canonical file pointed by the resourceFileName or null if the - * walking failed - * @throws IOException in case of issue when creating the canonical - * resulting file - * @see File#separatorChar - */ - private File walkPathComponents(String resourceFileName) throws IOException - { - StringTokenizer stringTokenizer = new StringTokenizer(resourceFileName, File.separator); - File currentFile = dir; - int tokenCount = stringTokenizer.countTokens(); - - for (int i = 0; i < tokenCount - 1; i++) - { - String currentToken = stringTokenizer.nextToken(); - - // If we are at the root directory and trying to go up, the walking is - // finished with an error - if ("..".equals(currentToken) && currentFile.equals(dir)) - return null; - - currentFile = new File(currentFile, currentToken); - - // If the current file doesn't exist or is not a directory, the walking is - // finished with an error - if (! (currentFile.exists() && currentFile.isDirectory())) - return null; - - } - - // Treat the last token differently, if it exists, because it does not need - // to be a directory - if (tokenCount > 0) - { - String currentToken = stringTokenizer.nextToken(); - - if ("..".equals(currentToken) && currentFile.equals(dir)) - return null; - - currentFile = new File(currentFile, currentToken); - - // If the current file doesn't exist, the walking is - // finished with an error - if (! currentFile.exists()) - return null; - } - - return currentFile.getCanonicalFile(); - } - } - - static final class FileResource extends Resource - { - final File file; - - FileResource(FileURLLoader loader, File file) - { - super(loader); - this.file = file; - } - - InputStream getInputStream() throws IOException - { - return new FileInputStream(file); - } - - public int getLength() - { - return (int) file.length(); - } - - public URL getURL() - { - try - { - return file.toURL(); - } - catch (MalformedURLException e) - { - InternalError ie = new InternalError(); - ie.initCause(e); - throw ie; - } - } - } - - // Constructors - - /** * Creates a URLClassLoader that gets classes from the supplied URLs. * To determine if this classloader may be created the constructor of * the super class (<code>SecureClassLoader</code>) is called first, which @@ -774,14 +264,8 @@ public class URLClassLoader extends SecureClassLoader this.factory = factory; addURLs(urls); - // If this factory is still not in factoryCache, add it, - // since we only support three protocols so far, 5 is enough - // for cache initial size - synchronized (factoryCache) - { - if (factory != null && factoryCache.get(factory) == null) - factoryCache.put(factory, new HashMap(5)); - } + // If this factory is still not in factoryCache, add it. + factoryCache.add(factory); } // Methods @@ -806,72 +290,114 @@ public class URLClassLoader extends SecureClassLoader // Reset the toString() value. thisString = null; - // Check global cache to see if there're already url loader - // for this url. - URLLoader loader = (URLLoader) urlloaders.get(newUrl); - if (loader == null) + // Create a loader for this URL. + URLLoader loader = null; + String file = newUrl.getFile(); + String protocol = newUrl.getProtocol(); + + // If we have a file: URL, we want to make it absolute + // here, before we decide whether it is really a jar. + URL absoluteURL; + if ("file".equals (protocol)) + { + File dir = new File(file); + URL absUrl; + try + { + absoluteURL = dir.getCanonicalFile().toURL(); + } + catch (IOException ignore) + { + try + { + absoluteURL = dir.getAbsoluteFile().toURL(); + } + catch (MalformedURLException _) + { + // This really should not happen. + absoluteURL = newUrl; + } + } + } + else { - String file = newUrl.getFile(); - String protocol = newUrl.getProtocol(); + // This doesn't hurt, and it simplifies the logic a + // little. + absoluteURL = newUrl; + } - // If we have a file: URL, we want to make it absolute - // here, before we decide whether it is really a jar. - URL absoluteURL; - if ("file".equals (protocol)) - { - File dir = new File(file); - URL absUrl; - try - { - absoluteURL = dir.getCanonicalFile().toURL(); - } - catch (IOException ignore) - { - try - { - absoluteURL = dir.getAbsoluteFile().toURL(); - } - catch (MalformedURLException _) - { - // This really should not happen. - absoluteURL = newUrl; - } - } - } - else - { - // This doesn't hurt, and it simplifies the logic a - // little. - absoluteURL = newUrl; - } + // First see if we can find a handler with the correct name. + try + { + Class handler = Class.forName(URL_LOADER_PREFIX + protocol); + Class[] argTypes = new Class[] { URLClassLoader.class, + URLStreamHandlerCache.class, + URLStreamHandlerFactory.class, + URL.class, + URL.class }; + Constructor k = handler.getDeclaredConstructor(argTypes); + loader + = (URLLoader) k.newInstance(new Object[] { this, + factoryCache, + factory, + newUrl, + absoluteURL }); + } + catch (ClassNotFoundException ignore) + { + // Fall through. + } + catch (NoSuchMethodException nsme) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't find URLLoader constructor"); + vme.initCause(nsme); + throw vme; + } + catch (InstantiationException inste) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't instantiate URLLoader"); + vme.initCause(inste); + throw vme; + } + catch (InvocationTargetException ite) + { + // Programming error in the class library. + InternalError vme + = new InternalError("error instantiating URLLoader"); + vme.initCause(ite); + throw vme; + } + catch (IllegalAccessException illae) + { + // Programming error in the class library. + InternalError vme + = new InternalError("invalid access to URLLoader"); + vme.initCause(illae); + throw vme; + } - // Check that it is not a directory + if (loader == null) + { + // If it is not a directory, use the jar loader. if (! (file.endsWith("/") || file.endsWith(File.separator))) - loader = new JarURLLoader(this, newUrl, absoluteURL); + loader = new JarURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); else if ("file".equals(protocol)) - loader = new FileURLLoader(this, newUrl, absoluteURL); + loader = new FileURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); else - loader = new RemoteURLLoader(this, newUrl); - - // Cache it. - urlloaders.put(newUrl, loader); + loader = new RemoteURLLoader(this, factoryCache, factory, + newUrl); } urlinfos.add(loader); - - Vector extraUrls = loader.getClassPath(); - if (extraUrls != null) - { - Iterator it = extraUrls.iterator(); - while (it.hasNext()) - { - URL url = (URL)it.next(); - URLLoader extraLoader = (URLLoader) urlloaders.get(url); - if (! urlinfos.contains (extraLoader)) - addURLImpl(url); - } - } - + ArrayList extra = loader.getClassPath(); + if (extra != null) + urlinfos.addAll(extra); } } @@ -987,7 +513,20 @@ public class URLClassLoader extends SecureClassLoader { // Just try to find the resource by the (almost) same name String resourceName = className.replace('.', '/') + ".class"; - Resource resource = findURLResource(resourceName); + int max = urlinfos.size(); + Resource resource = null; + for (int i = 0; i < max && resource == null; i++) + { + URLLoader loader = (URLLoader)urlinfos.elementAt(i); + if (loader == null) + continue; + + Class k = loader.getClass(className); + if (k != null) + return k; + + resource = loader.getResource(resourceName); + } if (resource == null) throw new ClassNotFoundException(className + " not found in " + this); @@ -1049,12 +588,13 @@ public class URLClassLoader extends SecureClassLoader if (packageName != null && getPackage(packageName) == null) { // define the package - Manifest manifest = resource.loader.getManifest(); + Manifest manifest = resource.getLoader().getManifest(); if (manifest == null) definePackage(packageName, null, null, null, null, null, null, null); else - definePackage(packageName, manifest, resource.loader.baseURL); + definePackage(packageName, manifest, + resource.getLoader().getBaseURL()); } // And finally construct the class! @@ -1166,34 +706,6 @@ public class URLClassLoader extends SecureClassLoader } /** - * If the URLStreamHandlerFactory has been set this return the appropriate - * URLStreamHandler for the given protocol, if not set returns null. - * - * @param protocol the protocol for which we need a URLStreamHandler - * @return the appropriate URLStreamHandler or null - */ - URLStreamHandler getURLStreamHandler(String protocol) - { - if (factory == null) - return null; - - URLStreamHandler handler; - synchronized (factoryCache) - { - // Check if there're handler for the same protocol in cache. - HashMap cache = (HashMap) factoryCache.get(factory); - handler = (URLStreamHandler) cache.get(protocol); - if (handler == null) - { - // Add it to cache. - handler = factory.createURLStreamHandler(protocol); - cache.put(protocol, handler); - } - } - return handler; - } - - /** * Finds all the resources with a particular name from all the locations. * * @param resourceName the name of the resource to lookup diff --git a/libjava/classpath/java/net/URLConnection.java b/libjava/classpath/java/net/URLConnection.java index 1f78dd8e8dd..28142b10aad 100644 --- a/libjava/classpath/java/net/URLConnection.java +++ b/libjava/classpath/java/net/URLConnection.java @@ -38,7 +38,6 @@ exception statement from your version. */ package java.net; -import gnu.classpath.NotImplementedException; import gnu.classpath.SystemProperties; import java.io.IOException; @@ -173,6 +172,11 @@ public abstract class URLConnection private static SimpleDateFormat[] dateFormats; private static boolean dateformats_initialized; + + /** + * The timeout period. + */ + private int timeout; /* Cached ParsePosition, used when parsing dates. */ private ParsePosition position; @@ -212,6 +216,38 @@ public abstract class URLConnection } /** + * Returns the connection timeout speed, in milliseconds, or zero if the timeout + * is infinite or not set. + * + * @return The timeout. + * + * @since 1.5 + */ + public int getConnectTimeout() + { + return timeout; + } + + /** + * Set the connection timeout speed, in milliseconds, or zero if the timeout + * is to be considered infinite. Note that in certain socket + * implementations/platforms this method may not have any effect. + * + * Throws an <code>IllegalArgumentException</code> if timeout < 0. + * + * @param timeout - The timeout, in milliseconds. + * + * @since 1.5 + */ + public void setConnectTimeout(int timeout) + throws IllegalArgumentException + { + if( timeout < 0 ) + throw new IllegalArgumentException("Timeout must be 0 or positive."); + this.timeout = timeout; + } + + /** * Returns the value of the content-length header field or -1 if the value * is not known or not present. * @@ -934,11 +970,12 @@ public abstract class URLConnection * @exception IOException If an error occurs */ public static String guessContentTypeFromStream(InputStream is) - throws IOException, NotImplementedException + throws IOException { - // See /etc/gnome-vfs-mime-magic or /etc/mime-magic for a reasonable - // idea of how to handle this. - return "application/octet-stream"; + String result = VMURLConnection.guessContentTypeFromStream(is); + if (result == null) + return "application/octet-stream"; + return result; } /** diff --git a/libjava/classpath/java/net/URLStreamHandler.java b/libjava/classpath/java/net/URLStreamHandler.java index ed95092219e..9a5d73ad0d0 100644 --- a/libjava/classpath/java/net/URLStreamHandler.java +++ b/libjava/classpath/java/net/URLStreamHandler.java @@ -369,13 +369,11 @@ public abstract class URLStreamHandler } /** - * Provides the default equals calculation. May be overidden by handlers for - * other protocols that have different requirements for equals(). This method - * requires that none of its arguments is null. This is guaranteed by the - * fact that it is only called by java.net.URL class. + * This is the default method for computing whether two URLs are + * equivalent. This method assumes that neither URL is null. * * @param url1 An URL object - * @param url2 An URL object + * @param url2 Another URL object * * @return True if both given URLs are equal, false otherwise. */ @@ -383,16 +381,21 @@ public abstract class URLStreamHandler { // This comparison is very conservative. It assumes that any // field can be null. - return (url1.getPort() == url2.getPort() + int port1 = url1.getPort(); + if (port1 == -1) + port1 = url1.getDefaultPort(); + int port2 = url2.getPort(); + if (port2 == -1) + port2 = url2.getDefaultPort(); + // Note that we don't bother checking the 'authority'; it is + // redundant. + return (port1 == port2 && ((url1.getProtocol() == null && url2.getProtocol() == null) || (url1.getProtocol() != null && url1.getProtocol().equals(url2.getProtocol()))) && ((url1.getUserInfo() == null && url2.getUserInfo() == null) || (url1.getUserInfo() != null && url1.getUserInfo().equals(url2.getUserInfo()))) - && ((url1.getAuthority() == null && url2.getAuthority() == null) - || (url1.getAuthority() != null - && url1.getAuthority().equals(url2.getAuthority()))) && ((url1.getHost() == null && url2.getHost() == null) || (url1.getHost() != null && url1.getHost().equals(url2.getHost()))) && ((url1.getPath() == null && url2.getPath() == null) diff --git a/libjava/classpath/java/nio/CharBuffer.java b/libjava/classpath/java/nio/CharBuffer.java index 6551555e20b..356a920eea0 100644 --- a/libjava/classpath/java/nio/CharBuffer.java +++ b/libjava/classpath/java/nio/CharBuffer.java @@ -107,14 +107,12 @@ public abstract class CharBuffer extends Buffer { // FIXME: implement better handling of java.lang.String. // Probably share data with String via reflection. - - if ((start < 0) - || (start > seq.length()) - || (end < start) - || (end > (seq.length() - start))) - throw new IndexOutOfBoundsException(); - + int len = end - start; + + if( len < 0 ) + throw new IndexOutOfBoundsException(); + char[] buffer = new char[len]; for (int i = 0; i < len; i++) diff --git a/libjava/classpath/java/nio/DirectByteBufferImpl.java b/libjava/classpath/java/nio/DirectByteBufferImpl.java index 53bb668aacf..3a9036f3148 100644 --- a/libjava/classpath/java/nio/DirectByteBufferImpl.java +++ b/libjava/classpath/java/nio/DirectByteBufferImpl.java @@ -232,6 +232,7 @@ abstract class DirectByteBufferImpl extends ByteBuffer private ByteBuffer duplicate(boolean readOnly) { int pos = position(); + if (this.mark != -1) reset(); int mark = position(); position(pos); diff --git a/libjava/classpath/java/nio/channels/FileChannel.java b/libjava/classpath/java/nio/channels/FileChannel.java index 0eefffbe97b..3aa19990917 100644 --- a/libjava/classpath/java/nio/channels/FileChannel.java +++ b/libjava/classpath/java/nio/channels/FileChannel.java @@ -114,12 +114,7 @@ public abstract class FileChannel extends AbstractInterruptibleChannel */ public final long write(ByteBuffer[] srcs) throws IOException { - long result = 0; - - for (int i = 0; i < srcs.length; i++) - result += write(srcs[i]); - - return result; + return write(srcs, 0, srcs.length); } /** @@ -169,12 +164,7 @@ public abstract class FileChannel extends AbstractInterruptibleChannel */ public final long read(ByteBuffer[] dsts) throws IOException { - long result = 0; - - for (int i = 0; i < dsts.length; i++) - read(dsts[i]); - - return result; + return read(dsts, 0, dsts.length); } /** diff --git a/libjava/classpath/java/nio/channels/SelectionKey.java b/libjava/classpath/java/nio/channels/SelectionKey.java index 5219b6bff84..9723faf5262 100644 --- a/libjava/classpath/java/nio/channels/SelectionKey.java +++ b/libjava/classpath/java/nio/channels/SelectionKey.java @@ -1,5 +1,5 @@ /* SelectionKey.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -60,7 +60,7 @@ public abstract class SelectionKey /** * Attaches obj to the key and returns the old attached object. */ - public final Object attach(Object obj) + public final synchronized Object attach(Object obj) { Object old = attached; attached = obj; @@ -70,7 +70,7 @@ public abstract class SelectionKey /** * Returns the object attached to the key. */ - public final Object attachment() + public final synchronized Object attachment() { return attached; } diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java index 5ab8468bf2e..02d09ce10ad 100644 --- a/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java @@ -1,5 +1,5 @@ /* AbstractSelectionKey.java -- - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -57,7 +57,7 @@ public abstract class AbstractSelectionKey extends SelectionKey /** * Cancels this key. */ - public final void cancel() + public final synchronized void cancel() { if (isValid()) { @@ -71,7 +71,7 @@ public abstract class AbstractSelectionKey extends SelectionKey * * @return true if this key is valid, false otherwise */ - public final boolean isValid() + public final synchronized boolean isValid() { return ! cancelled; } diff --git a/libjava/classpath/java/rmi/server/UID.java b/libjava/classpath/java/rmi/server/UID.java index 35963042211..940339e813a 100644 --- a/libjava/classpath/java/rmi/server/UID.java +++ b/libjava/classpath/java/rmi/server/UID.java @@ -94,23 +94,23 @@ public final class UID * The time stamp, when the UID was created. */ private long time; - + /** * Create the new UID that would have the described features of the * uniqueness. */ public UID() { - time = System.currentTimeMillis(); - unique = machineId; - if (time > last) - { - last = time; - count = uidCounter = Short.MIN_VALUE; - } - else + synchronized (UID.class) { - synchronized (UID.class) + time = System.currentTimeMillis(); + unique = machineId; + if (time > last) + { + last = time; + count = uidCounter = Short.MIN_VALUE; + } + else { if (uidCounter == Short.MAX_VALUE) { @@ -126,8 +126,7 @@ public final class UID uidCounter = Short.MIN_VALUE; time = last = System.currentTimeMillis(); } - - count = uidCounter++; + count = ++uidCounter; } } } @@ -210,7 +209,7 @@ public final class UID ^ hostIpHash; } - /** + /** * Get the string representation of this UID. * * @return a string, uniquely identifying this id. @@ -219,9 +218,8 @@ public final class UID { int max = Character.MAX_RADIX; // Translate into object count, counting from 0. - long lc = (count + Short.MIN_VALUE) & 0xFFFF; - return Long.toString(time, max) + ":" - + Long.toString(unique, max) + ":" + long lc = (count - Short.MIN_VALUE) & 0xFFFF; + return Long.toString(unique, max) + ":" + Long.toString(time, max) + "." + Long.toString(lc, max); } } diff --git a/libjava/classpath/java/security/Provider.java b/libjava/classpath/java/security/Provider.java index 4ffaa55bcb1..ea1dd9d3ff2 100644 --- a/libjava/classpath/java/security/Provider.java +++ b/libjava/classpath/java/security/Provider.java @@ -1,5 +1,5 @@ /* Provider.java -- Security provider information - Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2002, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,20 +41,19 @@ import java.io.Serializable; import java.util.Properties; /** - * This class represents a Java security architecture service provider. - * The services provided by a such a provider can range from security - * algorithms to key generation. + * This class represents a Java security architecture service provider. The + * services provided by a such a provider can range from security algorithms to + * key generation. * <p> - * Providers are installed by name and version number. There is one - * standard provider supplied with the class library. This is the - * "GNU" provider, which can also be accessed by the alias "SUN" for - * compatibility with the JDK. - * - * @version 0.0 - * + * Providers are installed by name and version number. See the static + * initializer of the {@link java.security.Security} class for the default + * security providers installed by this class library. + * * @author Aaron M. Renn (arenn@urbanophile.com) */ -public abstract class Provider extends Properties implements Serializable +public abstract class Provider + extends Properties + implements Serializable { private static final long serialVersionUID = -4298000515446427739L; @@ -119,36 +118,34 @@ public abstract class Provider extends Properties implements Serializable } /** - * Sets the key property to have the specified value. + * Maps a key property to a designated value. * <p> - * <bold>NOT IMPLEMENTED YET</bold>[ - * First, if there is a security manager, its <code>checkSecurityAccess</code> - * method is called with the string "putProviderProperty."+name, where name is - * the provider name, to see if it's ok to set this provider's property - * values. - * If the default implementation of <code>checkSecurityAccess</code> is used - * (that is, that method is not overriden), then this results in a call to the - * security manager's <code>checkPermission</code> method with a - * <code>SecurityPermission("putProviderProperty."+name)</code> - * permission.<br>] - * + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string <code>"putProviderProperty." + name</code>, where + * <code>name</code> is this provider's name. For the default implementation + * this translates into a {@link SecurityManager#checkPermission(Permission)} + * for a <code>SecurityPermission("putProviderProperty." + name)</code>. + * * @param key The property key. * @param value The property value. - * * @return The previous value of the specified property (<code>key</code>), * or <code>null</code> if it did not have one. - * @throws SecurityException If a security manager exists and its - * {@link java.lang.SecurityManager.checkSecurityAccess(java.lang.String)} - * method denies access to set property values. + * @throws SecurityException If a security manager is installed and its + * {@link SecurityManager#checkSecurityAccess(String)} method + * disallows adding properties at run-time. * @since Classpath 0.4+cvs, JDK 1.2 - * @see java.lang.Object.equals(Object) - * @see java.util.Hashtable.get(Object) + * @see java.lang.Object#equals(Object) + * @see java.util.Hashtable#get(Object) */ public Object put(Object key, Object value) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("putProviderProperty." + this.name); return super.put(toCanonicalKey(key), value); } - + // overrides same in java.util.Hashtable public Object get(Object key) { @@ -157,25 +154,45 @@ public abstract class Provider extends Properties implements Serializable /** * This method removes the specified key entry (and its associated value) - * from the property mapping list. + * from the property mapping collection. + * <p> + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string <code>"removeProviderProperty." + name</code>, where + * <code>name</code> is this provider's name. For the default implementation + * this translates into a {@link SecurityManager#checkPermission(Permission)} + * for a <code>SecurityPermission("removeProviderProperty." + name)</code>. * * @param key The key to remove - * * @return The previous value for this key, or <code>null</code> if no * previous value. */ public Object remove(Object key) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeProviderProperty." + this.name); return super.remove(toCanonicalKey(key)); } /** - * This method clears the entire property list such that it no longer + * This method clears the entire property collection such that it no longer * contains the properties used to look up the services provided by - * the <code>Provider</code>. + * this <code>Provider</code>. + * <p> + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string <code>"clearProviderProperties." + name</code>, + * where <code>name</code> is this provider's name. For the default + * implementation this translates into a + * {@link SecurityManager#checkPermission(Permission)} for a + * <code>SecurityPermission("clearProviderProperties." + name)</code>. */ public void clear() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("clearProviderProperties." + this.name); super.clear(); } @@ -191,12 +208,11 @@ public abstract class Provider extends Properties implements Serializable return (getClass().getName() + ": name=" + getName() + " version=" + version); } - + private Object toCanonicalKey(Object key) { if (key.getClass().isAssignableFrom(String.class)) // is it ours? return ((String) key).toUpperCase(); // use default locale - else - return key; + return key; } } diff --git a/libjava/classpath/java/security/SecureRandom.java b/libjava/classpath/java/security/SecureRandom.java index d403d496428..c66963e8f96 100644 --- a/libjava/classpath/java/security/SecureRandom.java +++ b/libjava/classpath/java/security/SecureRandom.java @@ -374,14 +374,9 @@ public class SecureRandom extends Random if (numBits == 0) return 0; - byte[] tmp = new byte[numBits / 8 + (1 * (numBits % 8))]; - - secureRandomSpi.engineNextBytes(tmp); - randomBytesUsed += tmp.length; - counter++; - + byte[] tmp = new byte[(numBits + 7) / 8]; + this.nextBytes(tmp); int ret = 0; - for (int i = 0; i < tmp.length; i++) ret |= (tmp[i] & 0xFF) << (8 * i); diff --git a/libjava/classpath/java/security/UnresolvedPermission.java b/libjava/classpath/java/security/UnresolvedPermission.java index d3f671a9c06..449454aaf85 100644 --- a/libjava/classpath/java/security/UnresolvedPermission.java +++ b/libjava/classpath/java/security/UnresolvedPermission.java @@ -201,6 +201,47 @@ public final class UnresolvedPermission extends Permission { return new UnresolvedPermissionCollection(); } + + /** + * Return the name of the class of the unresolved permission. + * @since 1.5 + */ + public String getUnresolvedType() + { + return type; + } + + /** + * Return the name of the unresolved permission. + * @since 1.5 + */ + public String getUnresolvedName() + { + return name; + } + + /** + * Return the actions of the unresolved permission, or null + * if there are no actions. + * @since 1.5 + */ + public String getUnresolvedActions() + { + return actions; + } + + /** + * Return the certificates of the unresolved permission. + * If there are no certificates, null is returned. Otherwise, + * a new array is returned. + * @since 1.5 + */ + public Certificate[] getUnresolvedCerts() + { + if (certs == null) + return null; + return (Certificate[]) certs.clone(); + } } // class UnresolvedPermission /** diff --git a/libjava/classpath/java/security/cert/X509CertSelector.java b/libjava/classpath/java/security/cert/X509CertSelector.java index a46d2288b8d..175e4c673c9 100644 --- a/libjava/classpath/java/security/cert/X509CertSelector.java +++ b/libjava/classpath/java/security/cert/X509CertSelector.java @@ -695,7 +695,7 @@ public class X509CertSelector implements CertSelector, Cloneable if (altNames == null) altNames = new LinkedList(); ArrayList l = new ArrayList(2); - l.add(new Integer(id)); + l.add(Integer.valueOf(id)); l.add(name); altNames.add(l); } @@ -714,7 +714,7 @@ public class X509CertSelector implements CertSelector, Cloneable if (altNames == null) altNames = new LinkedList(); ArrayList l = new ArrayList(2); - l.add(new Integer(id)); + l.add(Integer.valueOf(id)); l.add(name); altNames.add(l); } diff --git a/libjava/classpath/java/text/AttributedString.java b/libjava/classpath/java/text/AttributedString.java index c751ab43cf8..497b557fcf3 100644 --- a/libjava/classpath/java/text/AttributedString.java +++ b/libjava/classpath/java/text/AttributedString.java @@ -221,16 +221,13 @@ public class AttributedString // If the attribute run starts before the beginning index, we // need to junk it if it is an Annotation. Object attrib_obj = aci.getAttribute(attrib); - if (rs < begin) + rs -= begin; + if (rs < 0) { if (attrib_obj instanceof Annotation) continue; - rs = begin; - } - else - { - rs -= begin; + rs = 0; } // Create a map object. Yes this will only contain one attribute @@ -243,7 +240,7 @@ public class AttributedString c = aci.next(); } - while(c != CharacterIterator.DONE); + while( aci.getIndex() < end ); attribs = new AttributeRange[accum.size()]; attribs = (AttributeRange[]) accum.toArray(attribs); diff --git a/libjava/classpath/java/util/Arrays.java b/libjava/classpath/java/util/Arrays.java index 29134467b2b..1fa59594193 100644 --- a/libjava/classpath/java/util/Arrays.java +++ b/libjava/classpath/java/util/Arrays.java @@ -1,5 +1,5 @@ /* Arrays.java -- Utility class with methods to operate on arrays - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -97,7 +97,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final byte d = a[mid]; if (d == key) return mid; @@ -131,7 +131,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final char d = a[mid]; if (d == key) return mid; @@ -165,7 +165,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final short d = a[mid]; if (d == key) return mid; @@ -199,7 +199,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final int d = a[mid]; if (d == key) return mid; @@ -233,7 +233,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final long d = a[mid]; if (d == key) return mid; @@ -268,7 +268,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final int r = Float.compare(a[mid], key); if (r == 0) return mid; @@ -303,7 +303,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final int r = Double.compare(a[mid], key); if (r == 0) return mid; @@ -369,7 +369,7 @@ public class Arrays int mid = 0; while (low <= hi) { - mid = (low + hi) >> 1; + mid = (low + hi) >>> 1; final int d = Collections.compare(key, a[mid], c); if (d == 0) return mid; @@ -2341,8 +2341,10 @@ public class Arrays * value modification. The returned list implements both Serializable and * RandomAccess. * - * @param a the array to return a view of + * @param a the array to return a view of (<code>null</code> not permitted) * @return a fixed-size list, changes to which "write through" to the array + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. * @see Serializable * @see RandomAccess * @see Arrays.ArrayList diff --git a/libjava/classpath/java/util/Calendar.java b/libjava/classpath/java/util/Calendar.java index a324f5adabf..d4bbcd08e76 100644 --- a/libjava/classpath/java/util/Calendar.java +++ b/libjava/classpath/java/util/Calendar.java @@ -877,6 +877,7 @@ public abstract class Calendar implements Serializable, Cloneable 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, 0, 0, zone.getRawOffset(), 0 }; + complete(); isTimeSet = false; areFieldsSet = false; isSet[field] = false; diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java index dc37bad8a93..a2538cf1db8 100644 --- a/libjava/classpath/java/util/Collections.java +++ b/libjava/classpath/java/util/Collections.java @@ -1,5 +1,5 @@ /* Collections.java -- Utility class with methods to operate on collections - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -655,7 +655,7 @@ public class Collections boolean forward = true; while (low <= hi) { - pos = (low + hi) >> 1; + pos = (low + hi) >>> 1; if (i < pos) { if (!forward) @@ -684,7 +684,7 @@ public class Collections { while (low <= hi) { - pos = (low + hi) >> 1; + pos = (low + hi) >>> 1; final int d = compare(l.get(pos), key, c); if (d == 0) return pos; @@ -4817,6 +4817,87 @@ public class Collections private static final class UnmodifiableEntrySet extends UnmodifiableSet implements Serializable { + // Unmodifiable implementation of Map.Entry used as return value for + // UnmodifiableEntrySet accessors (iterator, toArray, toArray(Object[])) + private static final class UnmodifiableMapEntry + implements Map.Entry + { + private final Map.Entry e; + + private UnmodifiableMapEntry(Map.Entry e) + { + super(); + this.e = e; + } + + /** + * Returns <code>true</code> if the object, o, is also a map entry + * with an identical key and value. + * + * @param o the object to compare. + * @return <code>true</code> if o is an equivalent map entry. + */ + public boolean equals(Object o) + { + return e.equals(o); + } + + /** + * Returns the key of this map entry. + * + * @return the key. + */ + public Object getKey() + { + return e.getKey(); + } + + /** + * Returns the value of this map entry. + * + * @return the value. + */ + public Object getValue() + { + return e.getValue(); + } + + /** + * Computes the hash code of this map entry. The computation is + * described in the <code>Map</code> interface documentation. + * + * @return the hash code of this entry. + * @see Map#hashCode() + */ + public int hashCode() + { + return e.hashCode(); + } + + /** + * Blocks the alteration of the value of this map entry. This method + * never returns, throwing an exception instead. + * + * @param value The new value. + * @throws UnsupportedOperationException as an unmodifiable map entry + * does not support the <code>setValue()</code> operation. + */ + public Object setValue(Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a textual representation of the map entry. + * + * @return The map entry as a <code>String</code>. + */ + public String toString() + { + return e.toString(); + } + } + /** * Compatible with JDK 1.4. */ @@ -4846,80 +4927,46 @@ public class Collections public Object next() { final Map.Entry e = (Map.Entry) super.next(); - return new Map.Entry() - { - /** - * Returns <code>true</code> if the object, o, is also a map entry with an - * identical key and value. - * - * @param o the object to compare. - * @return <code>true</code> if o is an equivalent map entry. - */ - public boolean equals(Object o) - { - return e.equals(o); - } - - /** - * Returns the key of this map entry. - * - * @return the key. - */ - public Object getKey() - { - return e.getKey(); - } + return new UnmodifiableMapEntry(e); + } + }; + } - /** - * Returns the value of this map entry. - * - * @return the value. - */ - public Object getValue() - { - return e.getValue(); - } + // The array returned is an array of UnmodifiableMapEntry instead of + // Map.Entry + public Object[] toArray() + { + Object[] mapEntryResult = super.toArray(); + UnmodifiableMapEntry result[] = null; - /** - * Computes the hash code of this map entry. - * The computation is described in the <code>Map</code> - * interface documentation. - * - * @return the hash code of this entry. - * @see Map#hashCode() - */ - public int hashCode() + if (mapEntryResult != null) + { + result = new UnmodifiableMapEntry[mapEntryResult.length]; + for (int i = 0; i < mapEntryResult.length; i++) { - return e.hashCode(); + Map.Entry r = (Map.Entry) mapEntryResult[i]; + result[i] = new UnmodifiableMapEntry(r); } + } + return result; + } - /** - * Blocks the alteration of the value of this map entry. - * This method never returns, throwing an exception instead. - * - * @param value The new value. - * @throws UnsupportedOperationException as an unmodifiable - * map entry does not support the <code>setValue()</code> - * operation. - */ - public Object setValue(Object value) - { - throw new UnsupportedOperationException(); - } + // The array returned is an array of UnmodifiableMapEntry instead of + // Map.Entry + public Object[] toArray(Object[] array) + { + super.toArray(array); - /** - * Returns a textual representation of the map entry. - * - * @return The map entry as a <code>String</code>. - */ - public String toString() + if (array != null) + { + for (int i = 0; i < array.length; i++) { - return e.toString(); + array[i] = new UnmodifiableMapEntry((Map.Entry) array[i]); } - }; } - }; + return array; } + } // class UnmodifiableEntrySet /** diff --git a/libjava/classpath/java/util/DuplicateFormatFlagsException.java b/libjava/classpath/java/util/DuplicateFormatFlagsException.java new file mode 100644 index 00000000000..c180605cdc5 --- /dev/null +++ b/libjava/classpath/java/util/DuplicateFormatFlagsException.java @@ -0,0 +1,88 @@ +/* DuplicateFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} contain duplicates. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class DuplicateFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 18890531L; + + /** + * The flags which contain a duplicate. + * + * @serial the flags containing a duplicate. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new <code>DuplicateFormatFlagsException</code> + * which specifies that the supplied set of flags contains a + * duplicate. + * + * @param flags the flags containing a duplicate. + * @throws NullPointerException if <code>flags</code> is null. + */ + public DuplicateFormatFlagsException(String flags) + { + super("Duplicate flag passed in " + flags); + if (flags == null) + throw new + NullPointerException("Null flags value passed to constructor."); + this.flags = flags; + } + + /** + * Returns the flags which contain a duplicate. + * + * @return the flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java new file mode 100644 index 00000000000..ec317730503 --- /dev/null +++ b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java @@ -0,0 +1,111 @@ +/* FormatFlagsConversionMismatchException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} contains a flag that does not match + * the conversion character specified for it. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormatFlagsConversionMismatchException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19120414L; + + /** + * The mismatching flag. + * + * @serial the mismatching flag. + */ + // Note: name fixed by serialization. + private String f; + + /** + * The conversion character which doesn't match the + * appropriate flag. + * + * @serial the conversion character which doesn't match its flag. + */ + // Note: name fixed by serialization. + private char c; + + /** + * Constructs a new <code>FormatFlagsConversionMismatchException</code> + * which specifies that the flag, <code>f</code>, does + * not match its appropriate conversion character, <code>c</code>. + * + * @param f the mismatching flag. + * @param c the conversion character which doesn't match its flag. + * @throws NullPointerException if <code>f</code> is null. + */ + public FormatFlagsConversionMismatchException(String f, char c) + { + super("Invalid flag " + f + " for conversion " + c); + if (f == null) + throw new + NullPointerException("Null flag value passed to constructor."); + this.f = f; + this.c = c; + } + + /** + * Returns the conversion character which doesn't + * match the flag. + * + * @return the conversion character. + */ + public char getConversion() + { + return c; + } + + /** + * Returns the mismatching flag. + * + * @return the mismatching flag. + */ + public String getFlags() + { + return f; + } +} diff --git a/libjava/classpath/java/util/Formattable.java b/libjava/classpath/java/util/Formattable.java new file mode 100644 index 00000000000..27e26a701e4 --- /dev/null +++ b/libjava/classpath/java/util/Formattable.java @@ -0,0 +1,92 @@ +/* Formattable.java -- Objects which can be passed to a Formatter + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * <p> + * The <code>Formattable</code> interface is used to provide customised + * formatting to arbitrary objects via the {@link Formatter}. The + * {@link #formatTo} method is called for <code>Formattable</code> + * objects used with the 's' conversion operator, allowing the object + * to provide its own formatting of its internal data. + * </p> + * <p> + * Thread safety is left up to the implementing class. Thus, + * {@link Formattable} objects are not guaranteed to be thread-safe, + * and users should make their own provisions for multiple thread access. + * </p> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Formattable +{ + + /** + * Formats the object using the supplied formatter to the specification + * provided by the given flags, width and precision. + * + * @param formatter the formatter to use for formatting the object. + * The formatter gives access to the output stream + * and locale via {@link Formatter#out()} and + * {@link Formatter#locale()} respectively. + * @param flags a bit mask constructed from the flags in the + * {@link FormattableFlags} class. When no flags + * are set, the implementing class should use its + * defaults. + * @param width the minimum number of characters to include. + * A value of -1 indicates no minimum. The remaining + * space is padded with ' ' either on the left + * (the default) or right (if left justification is + * specified by the flags). + * @param precision the maximum number of characters to include. + * A value of -1 indicates no maximum. This value + * is applied prior to the minimum (the width). Thus, + * a value may meet the minimum width initially, but + * not when the width value is applied, due to + * characters being removed by the precision value. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + */ + public void formatTo(Formatter formatter, int flags, int width, + int precision); +} diff --git a/libjava/classpath/java/util/FormattableFlags.java b/libjava/classpath/java/util/FormattableFlags.java new file mode 100644 index 00000000000..648b3c03839 --- /dev/null +++ b/libjava/classpath/java/util/FormattableFlags.java @@ -0,0 +1,123 @@ +/* FormattableFlags.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class contains a set of flags used + * by the {@link Formattable#formatTo()} method. + * They are used to modify the output of the + * {@link Formattable}. The interpretation and + * validation of the flags is left to the + * particular {@link Formattable}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormattableFlags +{ + + /** + * Requires the output to be left-justified. Any spaces + * required to meet the specified width will be added to + * the right of the output. The default output is + * right-justified, where spaces are added to the left. + * The output is as for the format specifier + * '-' ('\u002d'). + */ + public static final int LEFT_JUSTIFY = 1; + + /** + * Requires the output to be in uppercase. The output + * should be the same as the result from calling + * {@link String#toUpperCase(java.util.Locale)} with + * the formatting locale. The output is as for the + * format specifier '^' ('\u005e'). + */ + public static final int UPPERCASE = 2; + + /** + * Requires the use of an alternate form, as specified + * in the documentation of {@link Formattable}. + * The output is as for the format specifier + * '#' ('\u0023'). + */ + public static final int ALTERNATE = 4; + + // Used internally by Formatter. + // Changes here must be reflected in the FLAGS string there. + + /** + * Requires the output to always include a '+' sign. + * The output is as for the format specifier '+'. + */ + static final int PLUS = 8; + + /** + * Requires the output to include a leading space on + * positive value. The output is as for the format + * specifier ' '. + */ + static final int SPACE = 16; + + /** + * Requires the output to be zero-padded. The output + * is as for the format specifier '0'. + */ + static final int ZERO = 32; + + /** + * Requires the output to include locale-specific + * grouping operators. The output is as for the + * format specifier ','. + */ + static final int COMMA = 64; + + /** + * Requires the output to include negative numbers + * enclosed in parentheses. The output is as for + * the format specifier '('. + */ + static final int PAREN = 128; + + // Not instantiable. + private FormattableFlags() + { + } +} diff --git a/libjava/classpath/java/util/Formatter.java b/libjava/classpath/java/util/Formatter.java new file mode 100644 index 00000000000..01d54631508 --- /dev/null +++ b/libjava/classpath/java/util/Formatter.java @@ -0,0 +1,1294 @@ +/* Formatter.java -- printf-style formatting + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.text.DateFormatSymbols; +import java.text.DecimalFormatSymbols; + +import gnu.classpath.SystemProperties; + +/** + * <p> + * A Java formatter for <code>printf</code>-style format strings, + * as seen in the C programming language. This differs from the + * C interpretation of such strings by performing much stricter + * checking of format specifications and their corresponding + * arguments. While unknown conversions will be ignored in C, + * and invalid conversions will only produce compiler warnings, + * the Java version utilises a full range of run-time exceptions to + * handle these cases. The Java version is also more customisable + * by virtue of the provision of the {@link Formattable} interface, + * which allows an arbitrary class to be formatted by the formatter. + * </p> + * <p> + * The formatter is accessible by more convienient static methods. + * For example, streams now have appropriate format methods + * (the equivalent of <code>fprintf</code>) as do <code>String</code> + * objects (the equivalent of <code>sprintf</code>). + * </p> + * <p> + * <strong>Note</strong>: the formatter is not thread-safe. For + * multi-threaded access, external synchronization should be provided. + * </p> + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public final class Formatter + implements Closeable, Flushable +{ + + /** + * The output of the formatter. + */ + private StringBuilder out; + + /** + * The locale used by the formatter. + */ + private Locale locale; + + /** + * Whether or not the formatter is closed. + */ + private boolean closed; + + /** + * The last I/O exception thrown by the output stream. + */ + private IOException ioException; + + // Some state used when actually formatting. + /** + * The format string. + */ + private String format; + + /** + * The current index into the string. + */ + private int index; + + /** + * The length of the format string. + */ + private int length; + + /** + * The formatting locale. + */ + private Locale fmtLocale; + + // Note that we include '-' twice. The flags are ordered to + // correspond to the values in FormattableFlags, and there is no + // flag (in the sense of this field used when parsing) for + // UPPERCASE; the second '-' serves as a placeholder. + /** + * A string used to index into the formattable flags. + */ + private static final String FLAGS = "--#+ 0,("; + + /** + * The system line separator. + */ + private static final String lineSeparator + = SystemProperties.getProperty("line.separator"); + + /** + * Constructs a new <code>Formatter</code> using the default + * locale and a {@link StringBuilder} as the output stream. + */ + public Formatter() + { + this(null, Locale.getDefault()); + } + + /** + * Constructs a new <code>Formatter</code> using the specified + * locale and a {@link StringBuilder} as the output stream. + * If the locale is <code>null</code>, then no localization + * is applied. + * + * @param loc the locale to use. + */ + public Formatter(Locale loc) + { + this(null, loc); + } + + /** + * Constructs a new <code>Formatter</code> using the default + * locale and the specified output stream. + * + * @param app the output stream to use. + */ + public Formatter(StringBuilder app) + { + this(app, Locale.getDefault()); + } + + /** + * Constructs a new <code>Formatter</code> using the specified + * locale and the specified output stream. If the locale is + * <code>null</code>, then no localization is applied. + * + * @param app the output stream to use. + * @param loc the locale to use. + */ + public Formatter(StringBuilder app, Locale loc) + { + this.out = app == null ? new StringBuilder() : app; + this.locale = loc; + } + + /** + * Closes the formatter, so as to release used resources. + * If the underlying output stream supports the {@link Closeable} + * interface, then this is also closed. Attempts to use + * a formatter instance, via any method other than + * {@link #ioException()}, after closure results in a + * {@link FormatterClosedException}. + */ + public void close() + { + if (closed) + return; + closed = true; + } + + /** + * Flushes the formatter, writing any cached data to the output + * stream. If the underlying output stream supports the + * {@link Flushable} interface, it is also flushed. + * + * @throws FormatterClosedException if the formatter is closed. + */ + public void flush() + { + if (closed) + throw new FormatterClosedException(); + } + + /** + * Return the name corresponding to a flag. + * + * @param flags the flag to return the name of. + * @return the name of the flag. + */ + private String getName(int flags) + { + // FIXME: do we want all the flags in here? + // Or should we redo how this is reported? + int bit = Integer.numberOfTrailingZeros(flags); + return FLAGS.substring(bit, bit + 1); + } + + /** + * Verify the flags passed to a conversion. + * + * @param flags the flags to verify. + * @param allowed the allowed flags mask. + * @param conversion the conversion character. + */ + private void checkFlags(int flags, int allowed, char conversion) + { + flags &= ~allowed; + if (flags != 0) + throw new FormatFlagsConversionMismatchException(getName(flags), + conversion); + } + + /** + * Throw an exception if a precision was specified. + * + * @param precision the precision value (-1 indicates not specified). + */ + private void noPrecision(int precision) + { + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + } + + /** + * Apply the numeric localization algorithm to a StringBuilder. + * + * @param builder the builder to apply to. + * @param flags the formatting flags to use. + * @param width the width of the numeric value. + * @param isNegative true if the value is negative. + */ + private void applyLocalization(StringBuilder builder, int flags, int width, + boolean isNegative) + { + DecimalFormatSymbols dfsyms; + if (fmtLocale == null) + dfsyms = new DecimalFormatSymbols(); + else + dfsyms = new DecimalFormatSymbols(fmtLocale); + + // First replace each digit. + char zeroDigit = dfsyms.getZeroDigit(); + int decimalOffset = -1; + for (int i = builder.length() - 1; i >= 0; --i) + { + char c = builder.charAt(i); + if (c >= '0' && c <= '9') + builder.setCharAt(i, (char) (c - '0' + zeroDigit)); + else if (c == '.') + { + assert decimalOffset == -1; + decimalOffset = i; + } + } + + // Localize the decimal separator. + if (decimalOffset != -1) + { + builder.deleteCharAt(decimalOffset); + builder.insert(decimalOffset, dfsyms.getDecimalSeparator()); + } + + // Insert the grouping separators. + if ((flags & FormattableFlags.COMMA) != 0) + { + char groupSeparator = dfsyms.getGroupingSeparator(); + int groupSize = 3; // FIXME + int offset = (decimalOffset == -1) ? builder.length() : decimalOffset; + // We use '>' because we don't want to insert a separator + // before the first digit. + for (int i = offset - groupSize; i > 0; i -= groupSize) + builder.insert(i, groupSeparator); + } + + if ((flags & FormattableFlags.ZERO) != 0) + { + // Zero fill. Note that according to the algorithm we do not + // insert grouping separators here. + for (int i = width - builder.length(); i > 0; --i) + builder.insert(0, zeroDigit); + } + + if (isNegative) + { + if ((flags & FormattableFlags.PAREN) != 0) + { + builder.insert(0, '('); + builder.append(')'); + } + else + builder.insert(0, '-'); + } + else if ((flags & FormattableFlags.PLUS) != 0) + builder.insert(0, '+'); + else if ((flags & FormattableFlags.SPACE) != 0) + builder.insert(0, ' '); + } + + /** + * A helper method that handles emitting a String after applying + * precision, width, justification, and upper case flags. + * + * @param arg the string to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void genericFormat(String arg, int flags, int width, int precision) + throws IOException + { + if ((flags & FormattableFlags.UPPERCASE) != 0) + { + if (fmtLocale == null) + arg = arg.toUpperCase(); + else + arg = arg.toUpperCase(fmtLocale); + } + + if (precision >= 0 && arg.length() > precision) + arg = arg.substring(0, precision); + + boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0; + if (leftJustify && width == -1) + throw new MissingFormatWidthException("fixme"); + if (! leftJustify && arg.length() < width) + { + for (int i = width - arg.length(); i > 0; --i) + out.append(' '); + } + out.append(arg); + if (leftJustify && arg.length() < width) + { + for (int i = width - arg.length(); i > 0; --i) + out.append(' '); + } + } + + /** + * Emit a boolean. + * + * @param arg the boolean to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void booleanFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + String result; + if (arg instanceof Boolean) + result = String.valueOf((Boolean) arg); + else + result = arg == null ? "false" : "true"; + genericFormat(result, flags, width, precision); + } + + /** + * Emit a hash code. + * + * @param arg the hash code to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void hashCodeFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + genericFormat(arg == null ? "null" : Integer.toHexString(arg.hashCode()), + flags, width, precision); + } + + /** + * Emit a String or Formattable conversion. + * + * @param arg the String or Formattable to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void stringFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + if (arg instanceof Formattable) + { + checkFlags(flags, + (FormattableFlags.LEFT_JUSTIFY + | FormattableFlags.UPPERCASE + | FormattableFlags.ALTERNATE), + conversion); + Formattable fmt = (Formattable) arg; + fmt.formatTo(this, flags, width, precision); + } + else + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + genericFormat(arg == null ? "null" : arg.toString(), flags, width, + precision); + } + } + + /** + * Emit a character. + * + * @param arg the character to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void characterFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + noPrecision(precision); + + int theChar; + if (arg instanceof Character) + theChar = ((Character) arg).charValue(); + else if (arg instanceof Byte) + theChar = (char) (((Byte) arg).byteValue ()); + else if (arg instanceof Short) + theChar = (char) (((Short) arg).shortValue ()); + else if (arg instanceof Integer) + { + theChar = ((Integer) arg).intValue(); + if (! Character.isValidCodePoint(theChar)) + throw new IllegalFormatCodePointException(theChar); + } + else + throw new IllegalFormatConversionException(conversion, arg.getClass()); + String result = new String(Character.toChars(theChar)); + genericFormat(result, flags, width, precision); + } + + /** + * Emit a '%'. + * + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void percentFormat(int flags, int width, int precision) + throws IOException + { + checkFlags(flags, FormattableFlags.LEFT_JUSTIFY, '%'); + noPrecision(precision); + genericFormat("%", flags, width, precision); + } + + /** + * Emit a newline. + * + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void newLineFormat(int flags, int width, int precision) + throws IOException + { + checkFlags(flags, 0, 'n'); + noPrecision(precision); + if (width != -1) + throw new IllegalFormatWidthException(width); + genericFormat(lineSeparator, flags, width, precision); + } + + /** + * Helper method to do initial formatting and checking for integral + * conversions. + * + * @param arg the formatted argument. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param radix the radix of the number. + * @param conversion the conversion character. + * @return the result. + */ + private StringBuilder basicIntegralConversion(Object arg, int flags, + int width, int precision, + int radix, char conversion) + { + assert radix == 8 || radix == 10 || radix == 16; + noPrecision(precision); + + // Some error checking. + if ((flags & FormattableFlags.ZERO) != 0 + && (flags & FormattableFlags.LEFT_JUSTIFY) == 0) + throw new IllegalFormatFlagsException(getName(flags)); + if ((flags & FormattableFlags.PLUS) != 0 + && (flags & FormattableFlags.SPACE) != 0) + throw new IllegalFormatFlagsException(getName(flags)); + + if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1) + throw new MissingFormatWidthException("fixme"); + + // Do the base translation of the value to a string. + String result; + int basicFlags = (FormattableFlags.LEFT_JUSTIFY + // We already handled any possible error when + // parsing. + | FormattableFlags.UPPERCASE + | FormattableFlags.ZERO); + if (radix == 10) + basicFlags |= (FormattableFlags.PLUS + | FormattableFlags.SPACE + | FormattableFlags.COMMA + | FormattableFlags.PAREN); + else + basicFlags |= FormattableFlags.ALTERNATE; + + if (arg instanceof BigInteger) + { + checkFlags(flags, + (basicFlags + | FormattableFlags.PLUS + | FormattableFlags.SPACE + | FormattableFlags.PAREN), + conversion); + BigInteger bi = (BigInteger) arg; + result = bi.toString(radix); + } + else if (arg instanceof Number + && ! (arg instanceof Float) + && ! (arg instanceof Double)) + { + checkFlags(flags, basicFlags, conversion); + long value = ((Number) arg).longValue (); + if (radix == 8) + result = Long.toOctalString(value); + else if (radix == 16) + result = Long.toHexString(value); + else + result = Long.toString(value); + } + else + throw new IllegalFormatConversionException(conversion, arg.getClass()); + + return new StringBuilder(result); + } + + /** + * Emit a hex or octal value. + * + * @param arg the hexadecimal or octal value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param radix the radix of the number. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void hexOrOctalConversion(Object arg, int flags, int width, + int precision, int radix, + char conversion) + throws IOException + { + assert radix == 8 || radix == 16; + + StringBuilder builder = basicIntegralConversion(arg, flags, width, + precision, radix, + conversion); + int insertPoint = 0; + + // Insert the sign. + if (builder.charAt(0) == '-') + { + // Already inserted. Note that we don't insert a sign, since + // the only case where it is needed it BigInteger, and it has + // already been inserted by toString. + ++insertPoint; + } + else if ((flags & FormattableFlags.PLUS) != 0) + { + builder.insert(insertPoint, '+'); + ++insertPoint; + } + else if ((flags & FormattableFlags.SPACE) != 0) + { + builder.insert(insertPoint, ' '); + ++insertPoint; + } + + // Insert the radix prefix. + if ((flags & FormattableFlags.ALTERNATE) != 0) + { + builder.insert(insertPoint, radix == 8 ? "0" : "0x"); + insertPoint += radix == 8 ? 1 : 2; + } + + // Now justify the result. + int resultWidth = builder.length(); + if (resultWidth < width) + { + char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' '; + if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0) + { + // Left justify. + if (fill == ' ') + insertPoint = builder.length(); + } + else + { + // Right justify. Insert spaces before the radix prefix + // and sign. + insertPoint = 0; + } + while (resultWidth++ < width) + builder.insert(insertPoint, fill); + } + + String result = builder.toString(); + if ((flags & FormattableFlags.UPPERCASE) != 0) + { + if (fmtLocale == null) + result = result.toUpperCase(); + else + result = result.toUpperCase(fmtLocale); + } + + out.append(result); + } + + /** + * Emit a decimal value. + * + * @param arg the hexadecimal or octal value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void decimalConversion(Object arg, int flags, int width, + int precision, char conversion) + throws IOException + { + StringBuilder builder = basicIntegralConversion(arg, flags, width, + precision, 10, + conversion); + boolean isNegative = false; + if (builder.charAt(0) == '-') + { + // Sign handling is done during localization. + builder.deleteCharAt(0); + isNegative = true; + } + + applyLocalization(builder, flags, width, isNegative); + genericFormat(builder.toString(), flags, width, precision); + } + + /** + * Emit a single date or time conversion to a StringBuilder. + * + * @param builder the builder to write to. + * @param cal the calendar to use in the conversion. + * @param conversion the formatting character to specify the type of data. + * @param syms the date formatting symbols. + */ + private void singleDateTimeConversion(StringBuilder builder, Calendar cal, + char conversion, + DateFormatSymbols syms) + { + int oldLen = builder.length(); + int digits = -1; + switch (conversion) + { + case 'H': + builder.append(cal.get(Calendar.HOUR_OF_DAY)); + digits = 2; + break; + case 'I': + builder.append(cal.get(Calendar.HOUR)); + digits = 2; + break; + case 'k': + builder.append(cal.get(Calendar.HOUR_OF_DAY)); + break; + case 'l': + builder.append(cal.get(Calendar.HOUR)); + break; + case 'M': + builder.append(cal.get(Calendar.MINUTE)); + digits = 2; + break; + case 'S': + builder.append(cal.get(Calendar.SECOND)); + digits = 2; + break; + case 'N': + // FIXME: nanosecond ... + digits = 9; + break; + case 'p': + { + int ampm = cal.get(Calendar.AM_PM); + builder.append(syms.getAmPmStrings()[ampm]); + } + break; + case 'z': + { + int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60); + builder.append(zone); + digits = 4; + // Skip the '-' sign. + if (zone < 0) + ++oldLen; + } + break; + case 'Z': + { + // FIXME: DST? + int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60); + String[][] zs = syms.getZoneStrings(); + builder.append(zs[zone + 12][1]); + } + break; + case 's': + { + long val = cal.getTime().getTime(); + builder.append(val / 1000); + } + break; + case 'Q': + { + long val = cal.getTime().getTime(); + builder.append(val); + } + break; + case 'B': + { + int month = cal.get(Calendar.MONTH); + builder.append(syms.getMonths()[month]); + } + break; + case 'b': + case 'h': + { + int month = cal.get(Calendar.MONTH); + builder.append(syms.getShortMonths()[month]); + } + break; + case 'A': + { + int day = cal.get(Calendar.DAY_OF_WEEK); + builder.append(syms.getWeekdays()[day]); + } + break; + case 'a': + { + int day = cal.get(Calendar.DAY_OF_WEEK); + builder.append(syms.getShortWeekdays()[day]); + } + break; + case 'C': + builder.append(cal.get(Calendar.YEAR) / 100); + digits = 2; + break; + case 'Y': + builder.append(cal.get(Calendar.YEAR)); + digits = 4; + break; + case 'y': + builder.append(cal.get(Calendar.YEAR) % 100); + digits = 2; + break; + case 'j': + builder.append(cal.get(Calendar.DAY_OF_YEAR)); + digits = 3; + break; + case 'm': + builder.append(cal.get(Calendar.MONTH) + 1); + digits = 2; + break; + case 'd': + builder.append(cal.get(Calendar.DAY_OF_MONTH)); + digits = 2; + break; + case 'e': + builder.append(cal.get(Calendar.DAY_OF_MONTH)); + break; + case 'R': + singleDateTimeConversion(builder, cal, 'H', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + break; + case 'T': + singleDateTimeConversion(builder, cal, 'H', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'S', syms); + break; + case 'r': + singleDateTimeConversion(builder, cal, 'I', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'S', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'p', syms); + break; + case 'D': + singleDateTimeConversion(builder, cal, 'm', syms); + builder.append('/'); + singleDateTimeConversion(builder, cal, 'd', syms); + builder.append('/'); + singleDateTimeConversion(builder, cal, 'y', syms); + break; + case 'F': + singleDateTimeConversion(builder, cal, 'Y', syms); + builder.append('-'); + singleDateTimeConversion(builder, cal, 'm', syms); + builder.append('-'); + singleDateTimeConversion(builder, cal, 'd', syms); + break; + case 'c': + singleDateTimeConversion(builder, cal, 'a', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'b', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'd', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'T', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'Z', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'Y', syms); + break; + default: + throw new UnknownFormatConversionException(String.valueOf(conversion)); + } + + if (digits > 0) + { + int newLen = builder.length(); + int delta = newLen - oldLen; + while (delta++ < digits) + builder.insert(oldLen, '0'); + } + } + + /** + * Emit a date or time value. + * + * @param arg the date or time value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @param subConversion the sub conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void dateTimeConversion(Object arg, int flags, int width, + int precision, char conversion, + char subConversion) + throws IOException + { + noPrecision(precision); + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + + Calendar cal; + if (arg instanceof Calendar) + cal = (Calendar) arg; + else + { + Date date; + if (arg instanceof Date) + date = (Date) arg; + else if (arg instanceof Long) + date = new Date(((Long) arg).longValue()); + else + throw new IllegalFormatConversionException(conversion, + arg.getClass()); + if (fmtLocale == null) + cal = Calendar.getInstance(); + else + cal = Calendar.getInstance(fmtLocale); + cal.setTime(date); + } + + // We could try to be more efficient by computing this lazily. + DateFormatSymbols syms; + if (fmtLocale == null) + syms = new DateFormatSymbols(); + else + syms = new DateFormatSymbols(fmtLocale); + + StringBuilder result = new StringBuilder(); + singleDateTimeConversion(result, cal, subConversion, syms); + + genericFormat(result.toString(), flags, width, precision); + } + + /** + * Advance the internal parsing index, and throw an exception + * on overrun. + * + * @throws IllegalArgumentException on overrun. + */ + private void advance() + { + ++index; + if (index >= length) + { + // FIXME: what exception here? + throw new IllegalArgumentException(); + } + } + + /** + * Parse an integer appearing in the format string. Will return -1 + * if no integer was found. + * + * @return the parsed integer. + */ + private int parseInt() + { + int start = index; + while (Character.isDigit(format.charAt(index))) + advance(); + if (start == index) + return -1; + return Integer.decode(format.substring(start, index)).intValue(); + } + + /** + * Parse the argument index. Returns -1 if there was no index, 0 if + * we should re-use the previous index, and a positive integer to + * indicate an absolute index. + * + * @return the parsed argument index. + */ + private int parseArgumentIndex() + { + int result = -1; + int start = index; + if (format.charAt(index) == '<') + { + result = 0; + advance(); + } + else if (Character.isDigit(format.charAt(index))) + { + result = parseInt(); + if (format.charAt(index) == '$') + advance(); + else + { + // Reset. + index = start; + result = -1; + } + } + return result; + } + + /** + * Parse a set of flags and return a bit mask of values from + * FormattableFlags. Will throw an exception if a flag is + * duplicated. + * + * @return the parsed flags. + */ + private int parseFlags() + { + int value = 0; + int start = index; + while (true) + { + int x = FLAGS.indexOf(format.charAt(index)); + if (x == -1) + break; + int newValue = 1 << x; + if ((value & newValue) != 0) + throw new DuplicateFormatFlagsException(format.substring(start, + index + 1)); + value |= newValue; + advance(); + } + return value; + } + + /** + * Parse the width part of a format string. Returns -1 if no width + * was specified. + * + * @return the parsed width. + */ + private int parseWidth() + { + return parseInt(); + } + + /** + * If the current character is '.', parses the precision part of a + * format string. Returns -1 if no precision was specified. + * + * @return the parsed precision. + */ + private int parsePrecision() + { + if (format.charAt(index) != '.') + return -1; + advance(); + int precision = parseInt(); + if (precision == -1) + // FIXME + throw new IllegalArgumentException(); + return precision; + } + + /** + * Outputs a formatted string based on the supplied specification, + * <code>fmt</code>, and its arguments using the specified locale. + * The locale of the formatter does not change as a result; the + * specified locale is just used for this particular formatting + * operation. If the locale is <code>null</code>, then no + * localization is applied. + * + * @param loc the locale to use for this format. + * @param fmt the format specification. + * @param args the arguments to apply to the specification. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + * @throws FormatterClosedException if the formatter is closed. + */ + public Formatter format(Locale loc, String fmt, Object[] args) + { + if (closed) + throw new FormatterClosedException(); + + // Note the arguments are indexed starting at 1. + int implicitArgumentIndex = 1; + int previousArgumentIndex = 0; + + try + { + fmtLocale = loc; + format = fmt; + length = format.length(); + for (index = 0; index < length; ++index) + { + char c = format.charAt(index); + if (c != '%') + { + out.append(c); + continue; + } + + int start = index; + advance(); + + // We do the needed post-processing of this later, when we + // determine whether an argument is actually needed by + // this conversion. + int argumentIndex = parseArgumentIndex(); + + int flags = parseFlags(); + int width = parseWidth(); + int precision = parsePrecision(); + char origConversion = format.charAt(index); + char conversion = origConversion; + if (Character.isUpperCase(conversion)) + { + flags |= FormattableFlags.UPPERCASE; + conversion = Character.toLowerCase(conversion); + } + + Object argument = null; + if (conversion == '%' || conversion == 'n') + { + if (argumentIndex != -1) + { + // FIXME: not sure about this. + throw new UnknownFormatConversionException("FIXME"); + } + } + else + { + if (argumentIndex == -1) + argumentIndex = implicitArgumentIndex++; + else if (argumentIndex == 0) + argumentIndex = previousArgumentIndex; + // Argument indices start at 1 but array indices at 0. + --argumentIndex; + if (argumentIndex < 0 || argumentIndex >= args.length) + throw new MissingFormatArgumentException(format.substring(start, index)); + argument = args[argumentIndex]; + } + + switch (conversion) + { + case 'b': + booleanFormat(argument, flags, width, precision, + origConversion); + break; + case 'h': + hashCodeFormat(argument, flags, width, precision, + origConversion); + break; + case 's': + stringFormat(argument, flags, width, precision, + origConversion); + break; + case 'c': + characterFormat(argument, flags, width, precision, + origConversion); + break; + case 'd': + checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'd'); + decimalConversion(argument, flags, width, precision, + origConversion); + break; + case 'o': + checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o'); + hexOrOctalConversion(argument, flags, width, precision, 8, + origConversion); + break; + case 'x': + hexOrOctalConversion(argument, flags, width, precision, 16, + origConversion); + case 'e': + // scientificNotationConversion(); + break; + case 'f': + // floatingDecimalConversion(); + break; + case 'g': + // smartFloatingConversion(); + break; + case 'a': + // hexFloatingConversion(); + break; + case 't': + advance(); + char subConversion = format.charAt(index); + dateTimeConversion(argument, flags, width, precision, + origConversion, subConversion); + break; + case '%': + percentFormat(flags, width, precision); + break; + case 'n': + newLineFormat(flags, width, precision); + break; + default: + throw new UnknownFormatConversionException(String.valueOf(origConversion)); + } + } + } + catch (IOException exc) + { + ioException = exc; + } + return this; + } + + /** + * Outputs a formatted string based on the supplied specification, + * <code>fmt</code>, and its arguments using the formatter's locale. + * + * @param fmt the format specification. + * @param args the arguments to apply to the specification. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + * @throws FormatterClosedException if the formatter is closed. + */ + public Formatter format(String format, Object[] args) + { + return format(locale, format, args); + } + + /** + * Returns the last I/O exception thrown by the + * <code>append()</code> operation of the underlying + * output stream. + * + * @return the last I/O exception. + */ + public IOException ioException() + { + return ioException; + } + + /** + * Returns the locale used by this formatter. + * + * @return the formatter's locale. + * @throws FormatterClosedException if the formatter is closed. + */ + public Locale locale() + { + if (closed) + throw new FormatterClosedException(); + return locale; + } + + /** + * Returns the output stream used by this formatter. + * + * @return the formatter's output stream. + * @throws FormatterClosedException if the formatter is closed. + */ + public StringBuilder out() + { + if (closed) + throw new FormatterClosedException(); + return out; + } + + /** + * Returns the result of applying {@link Object#toString()} + * to the underlying output stream. The results returned + * depend on the particular {@link Appendable} being used. + * For example, a {@link StringBuilder} will return the + * formatted output but an I/O stream will not. + * + * @throws FormatterClosedException if the formatter is closed. + */ + public String toString() + { + if (closed) + throw new FormatterClosedException(); + return out.toString(); + } +} diff --git a/libjava/classpath/java/util/FormatterClosedException.java b/libjava/classpath/java/util/FormatterClosedException.java new file mode 100644 index 00000000000..c3588648e34 --- /dev/null +++ b/libjava/classpath/java/util/FormatterClosedException.java @@ -0,0 +1,60 @@ +/* FormatterClosedException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a method is called on a {@link Formatter} but + * it has already been closed. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormatterClosedException + extends IllegalStateException +{ + private static final long serialVersionUID = 18111216L; + + /** + * Constructs a new <code>FormatterClosedException</code>. + */ + public FormatterClosedException() + { + } +} diff --git a/libjava/classpath/java/util/GregorianCalendar.java b/libjava/classpath/java/util/GregorianCalendar.java index 5ce053a3705..83ac00e77e0 100644 --- a/libjava/classpath/java/util/GregorianCalendar.java +++ b/libjava/classpath/java/util/GregorianCalendar.java @@ -445,7 +445,7 @@ public class GregorianCalendar extends Calendar if (isSet[WEEK_OF_MONTH]) { - int weeks = (month == 1 && leap == 0) ? 4 : 5; + int weeks = (month == 1 && leap == 0) ? 5 : 6; if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks) throw new IllegalArgumentException("Illegal WEEK_OF_MONTH."); } @@ -1164,7 +1164,7 @@ public class GregorianCalendar extends Calendar */ private static final int[] maximums = { - AD, 5000000, 11, 53, 5, 31, 366, + AD, 5000000, 11, 53, 6, 31, 366, SATURDAY, 5, PM, 12, 23, 59, 59, 999, +(12 * 60 * 60 * 1000), (12 * 60 * 60 * 1000) diff --git a/libjava/classpath/java/util/IllegalFormatCodePointException.java b/libjava/classpath/java/util/IllegalFormatCodePointException.java new file mode 100644 index 00000000000..001839fe1eb --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatCodePointException.java @@ -0,0 +1,85 @@ +/* IllegalFormatCodePointException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} receives a character with an + * invalid Unicode codepoint, as defined by + * {@link Character#isValidCodePoint(int)}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatCodePointException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19080630L; + + /** + * The character which is an invalid Unicode code point. + * + * @serial the invalid character. + */ + // Note: name fixed by serialization. + int c; + + /** + * Constructs a new <code>IllegalFormatCodePointException</code> + * which specifies that the character, <code>c</code>, passed to + * a {@link Formatter} is an invalid Unicode code point. + * + * @param c the invalid character. + */ + public IllegalFormatCodePointException(int c) + { + super("An invalid Unicode code point was supplied."); + this.c = c; + } + + /** + * Returns the invalid character. + * + * @return the invalid character. + */ + public int getCodePoint() + { + return c; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatConversionException.java b/libjava/classpath/java/util/IllegalFormatConversionException.java new file mode 100644 index 00000000000..2f981f26e45 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatConversionException.java @@ -0,0 +1,110 @@ +/* IllegalFormatConversionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the type of an argument supplied to the + * {@link Formatter#format()} method of a {@link Formatter} + * does not match the conversion character specified for it. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatConversionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 17000126L; + + /** + * The conversion character which doesn't match + * the type of the argument. + * + * @serial the conversion character. + */ + // Note: name fixed by serialization. + char c; + + /** + * The type of the mismatching argument. + * + * @serial the mismatching argument type. + */ + // Note: name fixed by serialization. + Class arg; + + /** + * Constructs a new <code>IllegalFormatConversionException</code> + * which specifies that the argument of type <code>arg</code> does + * not match the conversion character, <code>c</code>. + * + * @param c the conversion character. + * @param arg the type which doesn't match the conversion character. + * @throws NullPointerException if <code>arg</code> is null. + */ + public IllegalFormatConversionException(char c, Class arg) + { + super("The type, " + arg + ", is invalid for the conversion character, " + + c + "."); + if (arg == null) + throw new NullPointerException("The supplied type was null."); + this.c = c; + this.arg = arg; + } + + /** + * Returns the conversion character. + * + * @return the conversion character. + */ + public char getConversion() + { + return c; + } + + /** + * Returns the type of the mismatched argument. + * + * @return the type of the mismatched argument. + */ + public Class getArgumentClass() + { + return arg; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatException.java b/libjava/classpath/java/util/IllegalFormatException.java new file mode 100644 index 00000000000..4daafca4414 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatException.java @@ -0,0 +1,75 @@ +/* IllegalFormatException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A general exception thrown when a format string is supplied + * to a {@link Formatter} that contains either invalid syntax + * or a mismatch between the format specification and the + * supplied arguments. This class is never instantiated; + * instead one of its subclasses is used to throw a more + * specific exception. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatException + extends IllegalArgumentException +{ + private static final long serialVersionUID = 18830826L; + + /** + * Constructs a new <code>IllegalFormatException</code>. + */ + IllegalFormatException() + { + } + + /** + * Constructs a new <code>IllegalFormatException</code> + * with the specified message. + * + * @param msg the error message for this exception. + */ + IllegalFormatException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/IllegalFormatFlagsException.java b/libjava/classpath/java/util/IllegalFormatFlagsException.java new file mode 100644 index 00000000000..2a085c14113 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatFlagsException.java @@ -0,0 +1,86 @@ +/* IllegalFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} form an illegal combination. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 790824L; + + /** + * The set of flags which forms an illegal combination. + * + * @serial the illegal set of flags. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new <code>IllegalFormatFlagsException</code> + * for the specified flags. + * + * @param flags the illegal set of flags. + * @throws NullPointerException if <code>flags</code> is null. + */ + public IllegalFormatFlagsException(String flags) + { + super("An illegal set of flags, " + flags + ", was supplied."); + if (flags == null) + throw new NullPointerException("The supplied flags are null."); + this.flags = flags; + } + + /** + * Returns the illegal flags. + * + * @return the illegal flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatPrecisionException.java b/libjava/classpath/java/util/IllegalFormatPrecisionException.java new file mode 100644 index 00000000000..a555f5df425 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatPrecisionException.java @@ -0,0 +1,85 @@ +/* IllegalFormatPrecisionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the specified precision for a {@link Formatter} + * argument is illegal. This may be because the number is + * a negative number (other than -1), the argument does not + * accept a precision or for some other reason. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatPrecisionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 18711008L; + + /** + * The illegal precision value. + * + * @serial the illegal precision. + */ + // Note: name fixed by serialization. + private int p; + + /** + * Constructs a new <code>IllegalFormatPrecisionException</code> + * for the precision, <code>p</code>. + * + * @param p the illegal precision. + */ + public IllegalFormatPrecisionException(int p) + { + super("The precision, " + p + ", is illegal."); + this.p = p; + } + + /** + * Returns the illegal precision. + * + * @return the illegal precision. + */ + public int getPrecision() + { + return p; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatWidthException.java b/libjava/classpath/java/util/IllegalFormatWidthException.java new file mode 100644 index 00000000000..95d3e150282 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatWidthException.java @@ -0,0 +1,84 @@ +/* IllegalFormatWidthException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the specified width for a {@link Formatter} + * argument is illegal. This may be because the number is + * a negative number (other than -1) or for some other reason. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatWidthException + extends IllegalFormatException +{ + private static final long serialVersionUID = 16660902L; + + /** + * The illegal width value. + * + * @serial the illegal width. + */ + // Note: name fixed by serialization. + private int w; + + /** + * Constructs a new <code>IllegalFormatWidthException</code> + * with the specified width, <code>w</code>. + * + * @param w the illegal width. + */ + public IllegalFormatWidthException(int w) + { + super("The width, " + w + ", is illegal."); + this.w = w; + } + + /** + * Returns the illegal width. + * + * @return the illegal width. + */ + public int getWidth() + { + return w; + } +} diff --git a/libjava/classpath/java/util/InputMismatchException.java b/libjava/classpath/java/util/InputMismatchException.java new file mode 100644 index 00000000000..39326c33e0d --- /dev/null +++ b/libjava/classpath/java/util/InputMismatchException.java @@ -0,0 +1,73 @@ +/* InputMismatchException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Scanner} instance encounters a mismatch + * between the input data and the pattern it is trying to match it + * against. This could be because the input data represents an + * out-of-range value for the type the pattern represents, or simply + * because the data does not fit that particular type. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class InputMismatchException + extends NoSuchElementException +{ + /** + * Constructs a new <code>InputMismatchException</code> + * with a <code>null</code> message. + */ + public InputMismatchException() + { + } + + /** + * Constructs a new <code>InputMismatchException</code> + * with the supplied error message. + * + * @param s the error message to report back to the user. + */ + public InputMismatchException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/util/MissingFormatArgumentException.java b/libjava/classpath/java/util/MissingFormatArgumentException.java new file mode 100644 index 00000000000..4a9385ed5b5 --- /dev/null +++ b/libjava/classpath/java/util/MissingFormatArgumentException.java @@ -0,0 +1,90 @@ +/* MissingFormatArgumentException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the a format specification for a {@link Formatter} + * refers to an argument that is non-existent, or an argument index + * references a non-existent argument. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MissingFormatArgumentException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19190115L; + + /** + * The format specification which contains the + * unmatched argument. + * + * @serial the format specification. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new <code>MissingFormatArgumentException</code> + * for a format specification, <code>s</code>, which refers + * to a non-existent argument. + * + * @param s the format specification. + * @throws NullPointerException if <code>s</code> is null. + */ + public MissingFormatArgumentException(String s) + { + super("The specification, " + s + + ", refers to a non-existent argument."); + if (s == null) + throw new NullPointerException("The specification is null."); + this.s = s; + } + + /** + * Returns the format specification. + * + * @return the format specification. + */ + public String getFormatSpecifier() + { + return s; + } +} diff --git a/libjava/classpath/java/util/MissingFormatWidthException.java b/libjava/classpath/java/util/MissingFormatWidthException.java new file mode 100644 index 00000000000..55de6202f42 --- /dev/null +++ b/libjava/classpath/java/util/MissingFormatWidthException.java @@ -0,0 +1,88 @@ +/* MissingFormatWidthException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the a format specification for a {@link Formatter} + * does not include a width for a value where one is required. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MissingFormatWidthException + extends IllegalFormatException +{ + private static final long serialVersionUID = 15560123L; + + /** + * The format specification which contains the + * unmatched argument. + * + * @serial the format specification. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new <code>MissingFormatWidthException</code> + * for a format specification, <code>s</code>, which excludes + * a required width argument. + * + * @param s the format specification. + * @throws NullPointerException if <code>s</code> is null. + */ + public MissingFormatWidthException(String s) + { + super("The specification, " + s + ", misses a required width."); + if (s == null) + throw new NullPointerException("The specification is null."); + this.s = s; + } + + /** + * Returns the format specification. + * + * @return the format specification. + */ + public String getFormatSpecifier() + { + return s; + } +} diff --git a/libjava/classpath/java/util/UUID.java b/libjava/classpath/java/util/UUID.java new file mode 100644 index 00000000000..6a57d27b448 --- /dev/null +++ b/libjava/classpath/java/util/UUID.java @@ -0,0 +1,383 @@ +/* UUID.java -- Class that represents a UUID object. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * This class represents a 128-bit UUID value. + * + * There are several types of UUID, and while this class can be used to store + * them, only the Leach-Salz (variant 2) UUID specified in RFC-4122 will + * give meaningful results from the method calls. + * See: http://tools.ietf.org/html/4122 for the details + * + * The format of a Leach-Salz (variant 2) time-based (version 1) UUID + * is as follows: + * time_low - upper 32 bits of the most significant 64 bits, + * this is the least-significant part of the timestamp. + * + * time_mid - bits 16-31 of the most significant 64 bits, + * this is the middle portion of the timestamp. + * + * version - bits 8-15 of the most significant 64 bits. + * + * time_hi - bits 0-7 of the most significant 64 bits, + * the most significant portion of the timestamp. + * + * clock_and_reserved - bits 48-63 of the least significant 64 bits. + * a variable number of bits hold the variant + * (see the spec) + * + * node identifier - bits 0-47 of the least signficant 64 bits. + * + * These fields are valid only for version 1, in the remaining versions, + * only the version and variant fields are set, all others are used for data. + * + * @since 1.5 + * @author Sven de Marothy + */ +public final class UUID + extends Object + implements Serializable, Comparable // genericizeme! +{ + private static final long serialVersionUID = -4856846361193249489L; + + /** + * Serialized field - most significant 64 bits. + */ + private long mostSigBits; + + /** + * Serialized field - least significant 64 bits. + */ + private long leastSigBits; + + /** + * Random-number generator. + */ + private static transient Random r = new Random(); + + /** + * Constructs a new UUID. + * + * @since 1.5 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * Returns the clock-sequence value of this UUID. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns an int containing the clock-sequence value. + */ + public int clockSequence() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + return (int)((leastSigBits & 0x3FFF000000000000L) >> 48); + } + + /** + * Compare this UUID to another. + * The comparison is performed as between two 128-bit integers. + * + * @return -1 if this < val, 0 if they are equal, 1 if this > val. + */ + public int compareTo(Object val) + { + return compareTo((UUID)val); + } + + /** + * Compare this UUID to another. + * The comparison is performed as between two 128-bit integers. + * + * @return -1 if this < val, 0 if they are equal, 1 if this > val. + */ + public int compareTo(UUID o) + { + if( mostSigBits < o.mostSigBits ) + return -1; + if( mostSigBits > o.mostSigBits ) + return 1; + if( leastSigBits < o.leastSigBits ) + return -1; + if( leastSigBits > o.mostSigBits ) + return 1; + return 0; + } + + /** + * Compare a (UUID) object to this one + */ + public boolean equals(Object obj) + { + if( !(obj instanceof UUID ) ) + return false; + return ( ((UUID)obj).mostSigBits == mostSigBits && + ((UUID)obj).leastSigBits == leastSigBits ); + } + + /** + * Creates a UUID object from a Sting representation. + * + * For the format of the string, + * @see #toString() + * + * @return a new UUID object. + */ + public static UUID fromString(String name) + { + StringTokenizer st = new StringTokenizer( name.trim(), "-" ); + if( st.countTokens() < 5 ) + throw new IllegalArgumentException( "Incorrect UUID string"+ + " representation:"+name ); + + long msb = (Long.parseLong(st.nextToken(), 16) << 32); // time low + msb |= (Long.parseLong(st.nextToken(), 16) << 16); // time mid + msb |= Long.parseLong(st.nextToken(), 16); // time high + + long lsb = (Long.parseLong(st.nextToken(), 16) << 48); // clock + lsb |= Long.parseLong(st.nextToken(), 16); // node + + return new UUID(msb, lsb); + } + + /** + * Returns a String representation of the UUID. + * + * The format of the standard string representation (given in RFC4122) is: + * + * time-low "-" time-mid "-" + * time-high-and-version "-" + * clock-seq-and-reserved + * clock-seq-low "-" node + * + * Where each field is represented as a hex string. + * + * @return the String representation. + */ + public String toString() + { + return // time-low first + padHex( (( mostSigBits & 0xFFFFFFFF00000000L) >> 32) & 0xFFFFFFFFL, 8) + + "-" + // then time-mid + padHex( (( mostSigBits & 0xFFFF0000L ) >> 16), 4 ) + + "-" + // time-high + padHex( ( mostSigBits & 0x0000000000000000FFFFL ), 4 ) + + "-" + // clock (note - no reason to separate high and low here) + padHex( (((leastSigBits & 0xFFFF000000000000L) >> 48) & 0xFFFF), 4 ) + + "-" + // finally the node value. + padHex(leastSigBits & 0xFFFFFFFFFFFFL, 12); + } + + /** + * Returns the least significant 64 bits of the UUID as a <code>long</code>. + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * Returns the most significant 64 bits of the UUID as a <code>long</code>. + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * Returns a hash of this UUID. + */ + public int hashCode() + { + int l1 = (int)(leastSigBits & 0xFFFFFFFFL); + int l2 = (int)((leastSigBits & 0xFFFFFFFF00000000L) >> 32); + int m1 = (int)(mostSigBits & 0xFFFFFFFFL); + int m2 = (int)((mostSigBits & 0xFFFFFFFF00000000L) >> 32); + + return (l1 ^ l2) ^ (m1 ^ m2); + } + + /** + * Creates a UUID version 3 object (name based with MD5 hashing) + * from a series of bytes representing a name. + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + long msb, lsb; + byte[] hash; + + try + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + hash = md5.digest( name ); + } + catch (NoSuchAlgorithmException e) + { + throw new UnsupportedOperationException("No MD5 algorithm available."); + } + + msb = ((hash[0] & 0xFFL) << 56) | ((hash[1] & 0xFFL) << 48) | + ((hash[2] & 0xFFL) << 40) | ((hash[3] & 0xFFL) << 32) | + ((hash[4] & 0xFFL) << 24) | ((hash[5] & 0xFFL) << 16) | + ((hash[6] & 0xFFL) << 8) | (hash[7] & 0xFFL); + + lsb = ((hash[8] & 0xFFL) << 56) | ((hash[9] & 0xFFL) << 48) | + ((hash[10] & 0xFFL) << 40) | ((hash[11] & 0xFFL) << 32) | + ((hash[12] & 0xFFL) << 24) | ((hash[13] & 0xFFL) << 16) | + ((hash[14] & 0xFFL) << 8) | (hash[15] & 0xFFL); + + lsb &= 0x3FFFFFFFFFFFFFFFL; + lsb |= 0x8000000000000000L; // set top two bits to variant 2 + + msb &= 0xFFFFFFFFFFFF0FFFL; + msb |= 0x3000; // Version 3; + + return new UUID(msb, lsb); + } + + /** + * Returns the 48-bit node value in a long. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns a long with the node value in the lower 48 bits. + */ + public long node() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + return (leastSigBits & 0xFFFFFFFFFFFFL); + } + + /** + * Returns the 60-bit timestamp value of the UUID in a long. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns a long with the timestamp value. + */ + public long timestamp() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + long time = (( mostSigBits & 0xFFFFFFFF00000000L) >> 32); + time |= (( mostSigBits & 0xFFFF0000L ) << 16); + long time_hi = ( mostSigBits & 0xFFFL ); + time |= (time_hi << 48); + return time; + } + + /** + * Generate a Leach-Salz (Variant 2) randomly generated (version 4) + * UUID. + * + */ + public static UUID randomUUID() + { + long lsb = r.nextLong(); + long msb = r.nextLong(); + + lsb &= 0x3FFFFFFFFFFFFFFFL; + lsb |= 0x8000000000000000L; // set top two bits to variant 2 + + msb &= 0xFFFFFFFFFFFF0FFFL; + msb |= 0x4000; // Version 4; + + return new UUID( msb, lsb ); + } + + /** + * Returns a hex String from l, padded to n spaces. + */ + private String padHex( long l, int n ) + { + String s = Long.toHexString( l ); + while( s.length() < n ) + s = "0" + s; + return s; + } + + /** + * Returns the variant of the UUID + * + * This may be: + * 0 = Reserved for NCS backwards-compatibility + * 2 = Leach-Salz (supports the other methods in this class) + * 6 = Reserved for Microsoft backwards-compatibility + * 7 = (reserved for future use) + */ + public int variant() + { + // Get the top 3 bits (not all may be part of the variant) + int v = (int)((leastSigBits & 0xE000000000000000L) >> 61); + if( (v & 0x04) == 0 ) // msb of the variant is 0 + return 0; + if( (v & 0x02) == 0 ) // variant is 0 1 (Leach-Salz) + return 2; + return v; // 6 or 7 + } + + /** + * Returns the version # of the UUID. + * + * Valid version numbers for a variant 2 UUID are: + * 1 = Time based UUID + * 2 = DCE security UUID + * 3 = Name-based UUID using MD5 hashing + * 4 = Randomly generated UUID + * 5 = Name-based UUID using SHA-1 hashing + * + * @return the version number + */ + public int version() + { + return (int)((mostSigBits & 0xF000L) >> 12); + } +} diff --git a/libjava/classpath/java/util/UnknownFormatConversionException.java b/libjava/classpath/java/util/UnknownFormatConversionException.java new file mode 100644 index 00000000000..e37d9d356b2 --- /dev/null +++ b/libjava/classpath/java/util/UnknownFormatConversionException.java @@ -0,0 +1,86 @@ +/* UnknownFormatConversionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} is supplied with an + * unknown conversion. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class UnknownFormatConversionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19060418L; + + /** + * The unknown conversion. + * + * @serial the unknown conversion. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new <code>UnknownFormatConversionException</code> + * for the specified conversion string. + * + * @param s the conversion string. + * @throws NullPointerException if the conversion string is null. + */ + public UnknownFormatConversionException(String s) + { + super("Unknown format conversion: " + s); + if (s == null) + throw new NullPointerException("The conversion string is null."); + this.s = s; + } + + /** + * Returns the conversion string. + * + * @return the conversion string. + */ + public String getConversion() + { + return s; + } +} diff --git a/libjava/classpath/java/util/UnknownFormatFlagsException.java b/libjava/classpath/java/util/UnknownFormatFlagsException.java new file mode 100644 index 00000000000..b8f939ec42c --- /dev/null +++ b/libjava/classpath/java/util/UnknownFormatFlagsException.java @@ -0,0 +1,88 @@ +/* UnknownFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} is supplied with an + * unknown flag. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class UnknownFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19370506L; + + /** + * The set of flags containing the unknown flag. + * + * @serial the unknown conversion. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new <code>UnknownFormatFlagsException</code> + * which specifies that the supplied set of flags contains a + * unknown. + * + * @param flags the flags containing a unknown. + * @throws NullPointerException if <code>flags</code> is null. + */ + public UnknownFormatFlagsException(String s) + { + super("Unknown flag passed in " + s); + if (s == null) + throw new + NullPointerException("Null flags value passed to constructor."); + this.flags = s; + } + + /** + * Returns the flags which contain a unknown. + * + * @return the flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/Vector.java b/libjava/classpath/java/util/Vector.java index 67549f0c47d..eb72ae49df8 100644 --- a/libjava/classpath/java/util/Vector.java +++ b/libjava/classpath/java/util/Vector.java @@ -1,5 +1,6 @@ /* Vector.java -- Class that provides growable arrays. - Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005, 2006, + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,6 +38,7 @@ exception statement from your version. */ package java.util; + import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -479,7 +481,7 @@ public class Vector extends AbstractList } /** - * Removes the first (the lowestindex) occurance of the given object from + * Removes the first (the lowest index) occurrence of the given object from * the Vector. If such a remove was performed (the object was found), true * is returned. If there was no such object, false is returned. * diff --git a/libjava/classpath/java/util/jar/JarOutputStream.java b/libjava/classpath/java/util/jar/JarOutputStream.java index 2c8c2f08d8f..46c71b38c4c 100644 --- a/libjava/classpath/java/util/jar/JarOutputStream.java +++ b/libjava/classpath/java/util/jar/JarOutputStream.java @@ -101,7 +101,7 @@ public class JarOutputStream extends ZipOutputStream /** * Prepares the JarOutputStream for writing the next entry. - * This implementation just calls <code>super.putNextEntre()</code>. + * This implementation just calls <code>super.putNextEntry()</code>. * * @param entry The information for the next entry * @exception IOException when some unexpected I/O exception occurred diff --git a/libjava/classpath/java/util/jar/Manifest.java b/libjava/classpath/java/util/jar/Manifest.java index aa869f4c4b0..64a0c476a91 100644 --- a/libjava/classpath/java/util/jar/Manifest.java +++ b/libjava/classpath/java/util/jar/Manifest.java @@ -1,4 +1,4 @@ -/* Manifest.java -- Reads, writes and manipulaties jar manifest files +/* Manifest.java -- Reads, writes and manipulates jar manifest files Copyright (C) 2000, 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -152,7 +152,7 @@ public class Manifest implements Cloneable } /** - * Read and merge a <code>Mainfest</code> from the designated input stream. + * Read and merge a <code>Manifest</code> from the designated input stream. * * @param in the input stream to read from. * @throws IOException if an I/O related exception occurs during the process. diff --git a/libjava/classpath/java/util/logging/FileHandler.java b/libjava/classpath/java/util/logging/FileHandler.java index cde86191612..357d51e14fc 100644 --- a/libjava/classpath/java/util/logging/FileHandler.java +++ b/libjava/classpath/java/util/logging/FileHandler.java @@ -192,6 +192,42 @@ public class FileHandler extends StreamHandler { /** + * A literal that prefixes all file-handler related properties in the + * logging.properties file. + */ + private static final String PROPERTY_PREFIX = "java.util.logging.FileHandler"; + /** + * The name of the property to set for specifying a file naming (incl. path) + * pattern to use with rotating log files. + */ + private static final String PATTERN_KEY = PROPERTY_PREFIX + ".pattern"; + /** + * The default pattern to use when the <code>PATTERN_KEY</code> property was + * not specified in the logging.properties file. + */ + private static final String DEFAULT_PATTERN = "%h/java%u.log"; + /** + * The name of the property to set for specifying an approximate maximum + * amount, in bytes, to write to any one log output file. A value of zero + * (which is the default) implies a no limit. + */ + private static final String LIMIT_KEY = PROPERTY_PREFIX + ".limit"; + private static final int DEFAULT_LIMIT = 0; + /** + * The name of the property to set for specifying how many output files to + * cycle through. The default value is 1. + */ + private static final String COUNT_KEY = PROPERTY_PREFIX + ".count"; + private static final int DEFAULT_COUNT = 1; + /** + * The name of the property to set for specifying whether this handler should + * append, or not, its output to existing files. The default value is + * <code>false</code> meaning NOT to append. + */ + private static final String APPEND_KEY = PROPERTY_PREFIX + ".append"; + private static final boolean DEFAULT_APPEND = false; + + /** * The number of bytes a log file is approximately allowed to reach * before it is closed and the handler switches to the next file in * the rotating set. A value of zero means that files can grow @@ -252,16 +288,10 @@ public class FileHandler public FileHandler() throws IOException, SecurityException { - this(/* pattern: use configiguration */ null, - - LogManager.getIntProperty("java.util.logging.FileHandler.limit", - /* default */ 0), - - LogManager.getIntProperty("java.util.logging.FileHandler.count", - /* default */ 1), - - LogManager.getBooleanProperty("java.util.logging.FileHandler.append", - /* default */ false)); + this(LogManager.getLogManager().getProperty(PATTERN_KEY), + LogManager.getIntProperty(LIMIT_KEY, DEFAULT_LIMIT), + LogManager.getIntProperty(COUNT_KEY, DEFAULT_COUNT), + LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND)); } @@ -269,10 +299,7 @@ public class FileHandler public FileHandler(String pattern) throws IOException, SecurityException { - this(pattern, - /* limit */ 0, - /* count */ 1, - /* append */ false); + this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, DEFAULT_APPEND); } @@ -280,10 +307,7 @@ public class FileHandler public FileHandler(String pattern, boolean append) throws IOException, SecurityException { - this(pattern, - /* limit */ 0, - /* count */ 1, - append); + this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, append); } @@ -292,9 +316,7 @@ public class FileHandler throws IOException, SecurityException { this(pattern, limit, count, - LogManager.getBooleanProperty( - "java.util.logging.FileHandler.append", - /* default */ false)); + LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND)); } @@ -350,7 +372,7 @@ public class FileHandler throws IOException, SecurityException { super(/* output stream, created below */ null, - "java.util.logging.FileHandler", + PROPERTY_PREFIX, /* default level */ Level.ALL, /* formatter */ null, /* default formatter */ XMLFormatter.class); @@ -358,14 +380,14 @@ public class FileHandler if ((limit <0) || (count < 1)) throw new IllegalArgumentException(); - this.pattern = pattern; + this.pattern = pattern != null ? pattern : DEFAULT_PATTERN; this.limit = limit; this.count = count; this.append = append; this.written = 0; this.logFiles = new LinkedList (); - setOutputStream (createFileStream (pattern, limit, count, append, + setOutputStream (createFileStream (this.pattern, limit, count, append, /* generation */ 0)); } @@ -389,10 +411,9 @@ public class FileHandler * LogManager configuration property. */ if (pattern == null) - pattern = LogManager.getLogManager().getProperty( - "java.util.logging.FileHandler.pattern"); + pattern = LogManager.getLogManager().getProperty(PATTERN_KEY); if (pattern == null) - pattern = "%h/java%u.log"; + pattern = DEFAULT_PATTERN; if (count > 1 && !has (pattern, 'g')) pattern = pattern + ".%g"; diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java index e2604815b0c..e434651f87d 100644 --- a/libjava/classpath/java/util/logging/LogManager.java +++ b/libjava/classpath/java/util/logging/LogManager.java @@ -39,6 +39,8 @@ exception statement from your version. */ package java.util.logging; +import gnu.classpath.SystemProperties; + import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.ByteArrayInputStream; @@ -50,12 +52,11 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; -import gnu.classpath.SystemProperties; - /** * The <code>LogManager</code> maintains a hierarchical namespace * of Logger objects and manages properties for configuring the logging @@ -108,11 +109,23 @@ import gnu.classpath.SystemProperties; public class LogManager { /** + * The object name for the logging management bean. + * @since 1.5 + */ + public static final String LOGGING_MXBEAN_NAME + = "java.util.logging:type=Logging"; + + /** * The singleton LogManager instance. */ private static LogManager logManager; /** + * The singleton logging bean. + */ + private static LoggingMXBean loggingBean; + + /** * The registered named loggers; maps the name of a Logger to * a WeakReference to it. */ @@ -836,11 +849,11 @@ public class LogManager } catch (ClassNotFoundException e) { - warn(property, className, "class not found"); + warn(property, className, "class not found", e); } catch (IllegalAccessException e) { - warn(property, className, "illegal access"); + warn(property, className, "illegal access", e); } catch (InstantiationException e) { @@ -848,7 +861,7 @@ public class LogManager } catch (java.lang.LinkageError e) { - warn(property, className, "linkage error"); + warn(property, className, "linkage error", e); } return null; @@ -904,4 +917,63 @@ public class LogManager } } + /** + * Return the logging bean. There is a single logging bean per + * VM instance. + * @since 1.5 + */ + public static synchronized LoggingMXBean getLoggingMXBean() + { + if (loggingBean == null) + { + loggingBean = new LoggingMXBean() + { + public String getLoggerLevel(String logger) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + return null; + Level lev = l.getLevel(); + if (lev == null) + return ""; + return lev.getName(); + } + + public List getLoggerNames() + { + LogManager mgr = getLogManager(); + // This is inefficient, but perhaps better for maintenance. + return Collections.list(mgr.getLoggerNames()); + } + + public String getParentLoggerName(String logger) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + return null; + l = l.getParent(); + if (l == null) + return ""; + return l.getName(); + } + + public void setLoggerLevel(String logger, String level) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + throw new IllegalArgumentException("no logger named " + logger); + Level newLevel; + if (level == null) + newLevel = null; + else + newLevel = Level.parse(level); + l.setLevel(newLevel); + } + }; + } + return loggingBean; + } } diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java index 29f19e440ca..46588e542ee 100644 --- a/libjava/classpath/java/util/logging/Logger.java +++ b/libjava/classpath/java/util/logging/Logger.java @@ -1209,7 +1209,7 @@ public class Logger /** * Reset and close handlers attached to this logger. This function is package - * private because it must only be avaiable to the LogManager. + * private because it must only be available to the LogManager. */ void resetLogger() { diff --git a/libjava/classpath/java/util/logging/LoggingMXBean.java b/libjava/classpath/java/util/logging/LoggingMXBean.java new file mode 100644 index 00000000000..5f866c980d7 --- /dev/null +++ b/libjava/classpath/java/util/logging/LoggingMXBean.java @@ -0,0 +1,85 @@ +/* LoggingMxBean.java -- Management interface for logging + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.util.List; + +/** + * This interface represents the management interface for logging. + * There is a single logging bean per VM instance, which can be + * retrieved via {@link LogManager#getLoggingMXBean()}. + * + * @since 1.5 + */ +public interface LoggingMXBean +{ + /** + * Return the name of the logging level given the name of + * a logger. Returns null if no such logger exists. + * @param logger the logger's name + * @return the logging level's name, or null + */ + String getLoggerLevel(String logger); + + /** + * Return a list of all logger names. + */ + List/*<String>*/ getLoggerNames(); + + /** + * Return the name of the parent of the indicated logger. + * If no such logger exists, returns null. If the logger + * is the root logger, returns the empty string. + * @param logger the logger's name + * @return the name of the logger's parent, or null + */ + String getParentLoggerName(String logger); + + /** + * Sets the logging level for a particular logger. + * + * @param logger the name of the logger + * @param level the name of the new logging level, or null + * @throws IllegalArgumentException if the level is not + * recognized, or if the logger does not exist + * @throws SecurityException if access is denied; + * see {@link Logger#setLevel(Level)} + */ + void setLoggerLevel(String logger, String level); +} diff --git a/libjava/classpath/java/util/prefs/Preferences.java b/libjava/classpath/java/util/prefs/Preferences.java index a78381bfa1e..297759d88a4 100644 --- a/libjava/classpath/java/util/prefs/Preferences.java +++ b/libjava/classpath/java/util/prefs/Preferences.java @@ -37,6 +37,7 @@ exception statement from your version. */ package java.util.prefs; +import gnu.classpath.ServiceFactory; import gnu.java.util.prefs.NodeReader; import java.io.IOException; @@ -45,6 +46,7 @@ import java.io.OutputStream; import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; +import java.util.Iterator; /** * Preference node containing key value entries and subnodes. @@ -205,6 +207,17 @@ public abstract class Preferences { } }); + // Still no factory? Try to see if we have one defined + // as a System Preference + if (factory == null) + { + Iterator iter = ServiceFactory.lookupProviders + (PreferencesFactory.class, null); + + if (iter != null && iter.hasNext()) + factory = (PreferencesFactory) iter.next(); + } + // Still no factory? Use our default. if (factory == null) { diff --git a/libjava/classpath/java/util/regex/Matcher.java b/libjava/classpath/java/util/regex/Matcher.java index e86be25fc41..25e73810e95 100644 --- a/libjava/classpath/java/util/regex/Matcher.java +++ b/libjava/classpath/java/util/regex/Matcher.java @@ -38,9 +38,9 @@ exception statement from your version. */ package java.util.regex; -import gnu.regexp.RE; -import gnu.regexp.REMatch; -import gnu.regexp.CharIndexed; +import gnu.java.util.regex.CharIndexed; +import gnu.java.util.regex.RE; +import gnu.java.util.regex.REMatch; /** * Instance of a regular expression applied to a char sequence. diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java index 8c1998343a6..d716fa4e62d 100644 --- a/libjava/classpath/java/util/regex/Pattern.java +++ b/libjava/classpath/java/util/regex/Pattern.java @@ -37,9 +37,9 @@ exception statement from your version. */ package java.util.regex; -import gnu.regexp.RE; -import gnu.regexp.REException; -import gnu.regexp.RESyntax; +import gnu.java.util.regex.RE; +import gnu.java.util.regex.REException; +import gnu.java.util.regex.RESyntax; import java.io.Serializable; import java.util.ArrayList; @@ -73,12 +73,17 @@ public final class Pattern implements Serializable this.regex = regex; this.flags = flags; + RESyntax syntax = RESyntax.RE_SYNTAX_JAVA_1_4; int gnuFlags = 0; gnuFlags |= RE.REG_ICASE_USASCII; if ((flags & CASE_INSENSITIVE) != 0) gnuFlags |= RE.REG_ICASE; if ((flags & MULTILINE) != 0) - gnuFlags |= RE.REG_MULTILINE; + { + gnuFlags |= RE.REG_MULTILINE; + syntax = new RESyntax(syntax); + syntax.setLineSeparator(null); + } if ((flags & DOTALL) != 0) gnuFlags |= RE.REG_DOT_NEWLINE; if ((flags & UNICODE_CASE) != 0) @@ -86,7 +91,6 @@ public final class Pattern implements Serializable // not yet supported: // if ((flags & CANON_EQ) != 0) gnuFlags = - RESyntax syntax = RESyntax.RE_SYNTAX_JAVA_1_4; if ((flags & UNIX_LINES) != 0) { // Use a syntax set with \n for linefeeds? diff --git a/libjava/classpath/java/util/zip/ZipFile.java b/libjava/classpath/java/util/zip/ZipFile.java index 10c8365b833..47ced0fb84f 100644 --- a/libjava/classpath/java/util/zip/ZipFile.java +++ b/libjava/classpath/java/util/zip/ZipFile.java @@ -49,8 +49,8 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.util.Enumeration; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; /** * This class represents a Zip archive. You can ask for the contained @@ -88,7 +88,7 @@ public class ZipFile implements ZipConstants private final RandomAccessFile raf; // The entries of this zip file when initialized and not yet closed. - private HashMap entries; + private LinkedHashMap entries; private boolean closed = false; @@ -250,7 +250,7 @@ public class ZipFile implements ZipConstants throw new EOFException(name); int centralOffset = inp.readLeInt(); - entries = new HashMap(count+count/2); + entries = new LinkedHashMap(count+count/2); inp.seek(centralOffset); for (int i = 0; i < count; i++) @@ -347,7 +347,7 @@ public class ZipFile implements ZipConstants * @exception IllegalStateException when the ZipFile has already been closed. * @exception IOException when the entries could not be read. */ - private HashMap getEntries() throws IOException + private LinkedHashMap getEntries() throws IOException { synchronized(raf) { @@ -375,7 +375,7 @@ public class ZipFile implements ZipConstants try { - HashMap entries = getEntries(); + LinkedHashMap entries = getEntries(); ZipEntry entry = (ZipEntry) entries.get(name); // If we didn't find it, maybe it's a directory. if (entry == null && !name.endsWith("/")) @@ -414,7 +414,7 @@ public class ZipFile implements ZipConstants { checkClosed(); - HashMap entries = getEntries(); + LinkedHashMap entries = getEntries(); String name = entry.getName(); ZipEntry zipEntry = (ZipEntry) entries.get(name); if (zipEntry == null) |