diff options
Diffstat (limited to 'java')
43 files changed, 2695 insertions, 1731 deletions
diff --git a/java/awt/Color.java b/java/awt/Color.java index b03129241..c3d04c049 100644 --- a/java/awt/Color.java +++ b/java/awt/Color.java @@ -534,14 +534,31 @@ public class Color implements Paint, Serializable { // Do not inline getRGB() to this.value, because of SystemColor. int value = getRGB(); - int red = (value & RED_MASK) >> 16; - int green = (value & GREEN_MASK) >> 8; - int blue = value & BLUE_MASK; - // We have to special case 0-2 because they won't scale by division. - red = red < 3 ? 3 : (int) Math.min(255, red / BRIGHT_SCALE); - green = green < 3 ? 3 : (int) Math.min(255, green / BRIGHT_SCALE); - blue = blue < 3 ? 3 : (int) Math.min(255, blue / BRIGHT_SCALE); - return new Color(red, green, blue, 255); + int[] hues = new int[3]; + hues[0] = (value & RED_MASK) >> 16; + hues[1] = (value & GREEN_MASK) >> 8; + hues[2] = value & BLUE_MASK; + + // (0,0,0) is a special case. + if (hues[0] == 0 && hues[1] == 0 && hues[2] ==0) + { + hues[0] = 3; + hues[1] = 3; + hues[2] = 3; + } + else + { + for (int index = 0; index < 3; index++) + { + + if (hues[index] > 2) + hues[index] = (int) Math.min(255, hues[index]/0.7f); + if (hues[index] == 1 || hues[index] == 2) + hues[index] = 4; + } + } + + return new Color(hues[0], hues[1], hues[2], 255); } /** diff --git a/java/awt/Component.java b/java/awt/Component.java index ba669f8ec..26ba605b5 100644 --- a/java/awt/Component.java +++ b/java/awt/Component.java @@ -579,7 +579,7 @@ public abstract class Component transient ComponentPeer peer; /** The preferred component orientation. */ - transient ComponentOrientation orientation = ComponentOrientation.UNKNOWN; + transient ComponentOrientation componentOrientation = ComponentOrientation.UNKNOWN; /** * The associated graphics configuration. @@ -5244,8 +5244,8 @@ p * <li>the set of backward traversal keys public void setComponentOrientation(ComponentOrientation o) { - ComponentOrientation oldOrientation = orientation; - orientation = o; + ComponentOrientation oldOrientation = componentOrientation; + componentOrientation = o; firePropertyChange("componentOrientation", oldOrientation, o); } @@ -5257,7 +5257,7 @@ p * <li>the set of backward traversal keys */ public ComponentOrientation getComponentOrientation() { - return orientation; + return componentOrientation; } /** diff --git a/java/awt/Container.java b/java/awt/Container.java index e6d8493d3..17ca3e0ad 100644 --- a/java/awt/Container.java +++ b/java/awt/Container.java @@ -87,8 +87,6 @@ public class Container extends Component Component[] component; LayoutManager layoutMgr; - Dimension maxSize; - /** * @since 1.4 */ @@ -653,45 +651,39 @@ public class Container extends Component return; ContainerPeer cPeer = null; - if (peer != null && ! (peer instanceof LightweightPeer)) + if (peer instanceof ContainerPeer) { cPeer = (ContainerPeer) peer; cPeer.beginValidate(); } - for (int i = 0; i < ncomponents; ++i) - { - Component comp = component[i]; - - if (comp.getPeer () == null) - comp.addNotify(); - } - doLayout (); for (int i = 0; i < ncomponents; ++i) { Component comp = component[i]; - if (! comp.isValid()) + if (comp instanceof Container && ! (comp instanceof Window) + && ! comp.valid) { - if (comp instanceof Container) - { - ((Container) comp).validateTree(); - } - else - { - component[i].validate(); - } + ((Container) comp).validateTree(); + } + else + { + comp.validate(); } } + if (peer instanceof ContainerPeer) + { + cPeer = (ContainerPeer) peer; + cPeer.endValidate(); + } + /* children will call invalidate() when they are layed out. It is therefore important that valid is not set to true until after the children have been layed out. */ valid = true; - if (cPeer != null) - cPeer.endValidate(); } public void setFont(Font f) diff --git a/java/awt/MenuShortcut.java b/java/awt/MenuShortcut.java index adfd1d318..259cbf1ae 100644 --- a/java/awt/MenuShortcut.java +++ b/java/awt/MenuShortcut.java @@ -38,6 +38,8 @@ exception statement from your version. */ package java.awt; +import java.awt.event.KeyEvent; + /** * This class implements a keyboard accelerator for a menu item. * @@ -70,6 +72,8 @@ private int key; */ private boolean usesShift; +private String keyName; + /*************************************************************************/ /** @@ -99,6 +103,7 @@ MenuShortcut(int key, boolean usesShift) { this.key = key; this.usesShift = usesShift; + setKeyName(key); } /*************************************************************************/ @@ -181,7 +186,11 @@ equals(Object obj) public String toString() { - return(getClass().getName() + "[" + paramString () + "]"); + String temp = "Ctrl+"; + if (usesShift) + temp = temp + "Shift+"; + temp = temp + keyName; + return temp; } public int @@ -204,4 +213,224 @@ paramString() return "key=" + key + ",usesShift=" + usesShift; } -} // class MenuShortcut +private void +setKeyName(int key) +{ + if (key == '\n') + keyName = "Enter"; + else if (key == '\b') + keyName = "Backspace"; + else if (key == '\t') + keyName = "Tab"; + else if (key == ' ') + keyName = "Space"; + else if (key == ',') + keyName = "Comma"; + else if (key == '.') + keyName = "Period"; + else if (key == '/') + keyName = "Slash"; + else if (key == '\\') + keyName = "Back Slash"; + else if (key == ';') + keyName = "Semicolon"; + else if (key == '=') + keyName = "Equals"; + else if (key == '[') + keyName = "Open Bracket"; + else if (key == ']') + keyName = "Close Bracket"; + else if (key == '0') + keyName = "0"; + else if (key == '1') + keyName = "1"; + else if (key == '2') + keyName = "2"; + else if (key == '3') + keyName = "3"; + else if (key == '4') + keyName = "4"; + else if (key == '5') + keyName = "5"; + else if (key == '6') + keyName = "6"; + else if (key == '7') + keyName = "7"; + else if (key == '8') + keyName = "8"; + else if (key == '9') + keyName = "9"; + else if (key == 'A') + keyName = "A"; + else if (key == 'B') + keyName = "B"; + else if (key == 'C') + keyName = "C"; + else if (key == 'D') + keyName = "D"; + else if (key == 'E') + keyName = "E"; + else if (key == 'F') + keyName = "F"; + else if (key == 'G') + keyName = "G"; + else if (key == 'H') + keyName = "H"; + else if (key == 'I') + keyName = "I"; + else if (key == 'J') + keyName = "J"; + else if (key == 'K') + keyName = "K"; + else if (key == 'L') + keyName = "L"; + else if (key == 'M') + keyName = "M"; + else if (key == 'N') + keyName = "N"; + else if (key == 'O') + keyName = "O"; + else if (key == 'P') + keyName = "P"; + else if (key == 'Q') + keyName = "Q"; + else if (key == 'R') + keyName = "R"; + else if (key == 'S') + keyName = "S"; + else if (key == 'T') + keyName = "T"; + else if (key == 'U') + keyName = "U"; + else if (key == 'V') + keyName = "V"; + else if (key == 'W') + keyName = "W"; + else if (key == 'X') + keyName = "X"; + else if (key == 'Y') + keyName = "Y"; + else if (key == 'Z') + keyName = "Z"; + else if (key == 3) + keyName = "Cancel"; + else if (key == 12) + keyName = "Clear"; + else if (key == 16) + keyName = "Shift"; + else if (key == 17) + keyName = "Ctrl"; + else if (key == 18) + keyName = "Alt"; + else if (key == 19) + keyName = "Pause"; + else if (key == 20) + keyName = "Caps Lock"; + else if (key == 21) + keyName = "Kana"; + else if (key == 24) + keyName = "Final"; + else if (key == 25) + keyName = "Kanji"; + else if (key == 27) + keyName = "Escape"; + else if (key == 28) + keyName = "Convert"; + else if (key == 29) + keyName = "No Convert"; + else if (key == 30) + keyName = "Accept"; + else if (key == 31) + keyName = "Mode Change"; + else if (key == 33) + keyName = "Page Up"; + else if (key == 34) + keyName = "Page Down"; + else if (key == 35) + keyName = "End"; + else if (key == 36) + keyName = "Home"; + else if (key == 37) + keyName = "Left"; + else if (key == 38) + keyName = "Up"; + else if (key == 39) + keyName = "Right"; + else if (key == 40) + keyName = "Down"; + else if (key == 96) + keyName = "NumPad-0"; + else if (key == 97) + keyName = "NumPad-1"; + else if (key == 98) + keyName = "NumPad-2"; + else if (key == 99) + keyName = "NumPad-3"; + else if (key == 100) + keyName = "NumPad-4"; + else if (key == 101) + keyName = "NumPad-5"; + else if (key == 102) + keyName = "NumPad-6"; + else if (key == 103) + keyName = "NumPad-7"; + else if (key == 104) + keyName = "NumPad-8"; + else if (key == 105) + keyName = "NumPad-9"; + else if (key == 106) + keyName = "NumPad *"; + else if (key == 107) + keyName = "NumPad +"; + else if (key == 108) + keyName = "NumPad ,"; + else if (key == 109) + keyName = "NumPad -"; + else if (key == 110) + keyName = "NumPad ."; + else if (key == 111) + keyName = "NumPad /"; + else if (key == 112) + keyName = "F1"; + else if (key == 113) + keyName = "F2"; + else if (key == 114) + keyName = "F3"; + else if (key == 115) + keyName = "F4"; + else if (key == 116) + keyName = "F5"; + else if (key == 117) + keyName = "F6"; + else if (key == 118) + keyName = "F7"; + else if (key == 119) + keyName = "F8"; + else if (key == 120) + keyName = "F9"; + else if (key == 121) + keyName = "F10"; + else if (key == 122) + keyName = "F11"; + else if (key == 123) + keyName = "F12"; + else if (key == 127) + keyName = "Delete"; + else if (key == 144) + keyName = "Num Lock"; + else if (key == 145) + keyName = "Scroll Lock"; + else if (key == 154) + keyName = "Print Screen"; + else if (key == 155) + keyName = "Insert"; + else if (key == 156) + keyName = "Help"; + else if (key == 157) + keyName = "Meta"; + else if (key == 192) + keyName = "Back Quote"; + else if (key == 222) + keyName = "Quote"; +} +} // class MenuShortcut diff --git a/java/awt/dnd/DropTargetDragEvent.java b/java/awt/dnd/DropTargetDragEvent.java index 838141ec3..58feb4387 100644 --- a/java/awt/dnd/DropTargetDragEvent.java +++ b/java/awt/dnd/DropTargetDragEvent.java @@ -147,7 +147,6 @@ public class DropTargetDragEvent extends DropTargetEvent */ public Transferable getTransferable() { - // FIXME: Not implemented - return null; + return context.getTransferable(); } } // class DropTargetDragEvent diff --git a/java/awt/image/AffineTransformOp.java b/java/awt/image/AffineTransformOp.java index bb4b79523..7820684d2 100644 --- a/java/awt/image/AffineTransformOp.java +++ b/java/awt/image/AffineTransformOp.java @@ -1,6 +1,6 @@ /* AffineTransformOp.java -- This class performs affine transformation between two images or rasters in 2 dimensions. - Copyright (C) 2004 Free Software Foundation + Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -52,6 +52,7 @@ import java.util.Arrays; * rasters in 2 dimensions. * * @author Olga Rodimina (rodimina@redhat.com) + * @author Francis Kung (fkung@redhat.com) */ public class AffineTransformOp implements BufferedImageOp, RasterOp { @@ -74,6 +75,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * * @param xform AffineTransform that will applied to the source image * @param interpolationType type of interpolation used + * @throws ImagingOpException if the transform matrix is noninvertible */ public AffineTransformOp (AffineTransform xform, int interpolationType) { @@ -102,6 +104,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * * @param xform AffineTransform that will applied to the source image * @param hints rendering hints that will be used during transformation + * @throws ImagingOpException if the transform matrix is noninvertible */ public AffineTransformOp (AffineTransform xform, RenderingHints hints) { @@ -115,8 +118,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * Creates empty BufferedImage with the size equal to that of the * transformed image and correct number of bands. The newly created * image is created with the specified ColorModel. - * If the ColorModel is equal to null, then image is created - * with the ColorModel of the source image. + * If the ColorModel is equal to null, an appropriate ColorModel is used. * * @param src source image * @param destCM color model for the destination image @@ -125,17 +127,21 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp public BufferedImage createCompatibleDestImage (BufferedImage src, ColorModel destCM) { + if (destCM != null) + return new BufferedImage(destCM, + createCompatibleDestRaster(src.getRaster()), + src.isAlphaPremultiplied(), null); + + // This behaviour was determined by Mauve testcases, and is compatible + // with the reference implementation + if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE + || src.getType() == BufferedImage.TYPE_4BYTE_ABGR + || src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE) + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); - // if destCm is not specified, use color model of the source image - - if (destCM == null) - destCM = src.getColorModel (); - - return new BufferedImage (destCM, - createCompatibleDestRaster (src.getRaster ()), - src.isAlphaPremultiplied (), - null); - + else + return new BufferedImage(src.getWidth(), src.getHeight(), + BufferedImage.TYPE_INT_ARGB); } /** @@ -148,7 +154,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public WritableRaster createCompatibleDestRaster (Raster src) { - Rectangle rect = (Rectangle) getBounds2D (src); + Rectangle2D rect = getBounds2D(src); // throw RasterFormatException if resulting width or height of the // transformed raster is 0 @@ -156,33 +162,31 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp if (rect.getWidth () == 0 || rect.getHeight () == 0) throw new RasterFormatException("width or height is 0"); - return src.createCompatibleWritableRaster ((int) rect.getWidth (), - (int) rect.getHeight ()); + return src.createCompatibleWritableRaster((int) rect.getWidth(), + (int) rect.getHeight()); } /** * Transforms source image using transform specified at the constructor. - * The resulting transformed image is stored in the destination image. + * The resulting transformed image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. * * @param src source image * @param dst destination image + * @throws IllegalArgumentException if the source and destination image are + * the same * @return transformed source image */ public final BufferedImage filter (BufferedImage src, BufferedImage dst) { if (dst == src) - throw new IllegalArgumentException ("src image cannot be the same as the dst image"); - - // If the destination image is null, then BufferedImage is - // created with ColorModel of the source image + throw new IllegalArgumentException ("src image cannot be the same as " + + "the dst image"); + // If the destination image is null, then use a compatible BufferedImage if (dst == null) - dst = createCompatibleDestImage(src, src.getColorModel ()); - - // FIXME: Must check if color models of src and dst images are the same. - // If it is not, then source image should be converted to color model - // of the destination image + dst = createCompatibleDestImage(src, null); Graphics2D gr = (Graphics2D) dst.createGraphics (); gr.setRenderingHints (hints); @@ -193,10 +197,13 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp /** * Transforms source raster using transform specified at the constructor. - * The resulting raster is stored in the destination raster. + * The resulting raster is stored in the destination raster if it is not + * null, otherwise a new raster is created and returned. * * @param src source raster * @param dst destination raster + * @throws IllegalArgumentException if the source and destination are not + * compatible * @return transformed raster */ public final WritableRaster filter (Raster src, WritableRaster dst) @@ -212,85 +219,47 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp throw new IllegalArgumentException("src and dst must have same number" + " of bands"); - double[] dpts = new double[dst.getWidth() * 2]; - double[] pts = new double[dst.getWidth() * 2]; + // Create arrays to hold all the points + double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2]; + double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2]; + + // Populate array with all points in the *destination* raster + int i = 0; for (int x = 0; x < dst.getWidth(); x++) - { - dpts[2 * x] = x + dst.getMinX(); - dpts[2 * x + 1] = x; - } - Rectangle srcbounds = src.getBounds(); - if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) - { - for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) - { - try { - transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); - } catch (NoninvertibleTransformException e) { - // Can't happen since the constructor traps this - e.printStackTrace(); - } - - for (int x = 0; x < dst.getWidth(); x++) - { - if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) - continue; - dst.setDataElements(x + dst.getMinX(), y, - src.getDataElements((int)pts[2 * x], - (int)pts[2 * x + 1], - null)); - } - } - } - else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) - { - double[] tmp = new double[4 * src.getNumBands()]; - for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) { - try { - transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); - } catch (NoninvertibleTransformException e) { - // Can't happen since the constructor traps this - e.printStackTrace(); - } - - for (int x = 0; x < dst.getWidth(); x++) - { - if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) - continue; - int xx = (int)pts[2 * x]; - int yy = (int)pts[2 * x + 1]; - double dx = (pts[2 * x] - xx); - double dy = (pts[2 * x + 1] - yy); - - // TODO write this more intelligently - if (xx == src.getMinX() + src.getWidth() - 1 || - yy == src.getMinY() + src.getHeight() - 1) + for (int y = 0; y < dst.getHeight(); y++) { - // bottom or right edge - Arrays.fill(tmp, 0); - src.getPixel(xx, yy, tmp); + dstPts[i++] = x; + dstPts[i++] = y; } - else - { - // Normal case - src.getPixels(xx, yy, 2, 2, tmp); - for (int b = 0; b < src.getNumBands(); b++) - tmp[b] = dx * dy * tmp[b] - + (1 - dx) * dy * tmp[b + src.getNumBands()] - + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()] - + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()]; - } - dst.setPixel(x, y, tmp); - } } - } - else - { - // Bicubic - throw new UnsupportedOperationException("not implemented yet"); - } + Rectangle srcbounds = src.getBounds(); + + // Use an inverse transform to map each point in the destination to + // a point in the source. Note that, while all points in the destination + // matrix are integers, this is not necessarily true for points in the + // source (hence why interpolation is required) + try + { + AffineTransform inverseTx = transform.createInverse(); + inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2); + } + catch (NoninvertibleTransformException e) + { + // Shouldn't happen since the constructor traps this + throw new ImagingOpException(e.getMessage()); + } + + // Different interpolation methods... + if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) + filterNearest(src, dst, dstPts, srcPts); + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + filterBilinear(src, dst, dstPts, srcPts); + + else // bicubic + filterBicubic(src, dst, dstPts, srcPts); + return dst; } @@ -314,16 +283,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public final Rectangle2D getBounds2D (Raster src) { - // determine new size for the transformed raster. - // Need to calculate transformed coordinates of the lower right - // corner of the raster. The upper left corner is always (0,0) - - double x2 = (double) src.getWidth () + src.getMinX (); - double y2 = (double) src.getHeight () + src.getMinY (); - Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null); - - Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ()); - return rect.getBounds (); + return transform.createTransformedShape(src.getBounds()).getBounds2D(); } /** @@ -333,8 +293,12 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public final int getInterpolationType () { - if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) return TYPE_BILINEAR; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) + return TYPE_BICUBIC; + else return TYPE_NEAREST_NEIGHBOR; } @@ -372,4 +336,191 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { return transform; } + + /** + * Perform nearest-neighbour filtering + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterNearest(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points on the destination raster, copy the value from the + // corrosponding (rounded) source point + for (int i = 0; i < dpts.length; i += 2) + { + int srcX = (int) Math.round(pts[i]) + src.getMinX(); + int srcY = (int) Math.round(pts[i + 1]) + src.getMinY(); + + if (srcbounds.contains(srcX, srcY)) + dst.setDataElements((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + src.getDataElements(srcX, srcY, null)); + } + } + + /** + * Perform bilinear filtering + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterBilinear(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points in the destination raster, use bilinear interpolation + // to find the value from the corrosponding source points + for (int i = 0; i < dpts.length; i += 2) + { + int srcX = (int) Math.round(pts[i]) + src.getMinX(); + int srcY = (int) Math.round(pts[i + 1]) + src.getMinY(); + + if (srcbounds.contains(srcX, srcY)) + { + // Corner case at the bottom or right edge; use nearest neighbour + if (pts[i] >= src.getWidth() - 1 + || pts[i + 1] >= src.getHeight() - 1) + dst.setDataElements((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + src.getDataElements(srcX, srcY, null)); + + // Standard case, apply the bilinear formula + else + { + int x = (int) Math.floor(pts[i] + src.getMinX()); + int y = (int) Math.floor(pts[i + 1] + src.getMinY()); + double xdiff = pts[i] + src.getMinX() - x; + double ydiff = pts[i + 1] + src.getMinY() - y; + + // Run the interpolation for each band + for (int j = 0; j < src.getNumBands(); j++) + { + double result = (src.getSampleDouble(x, y, j) * (1 - xdiff) + + src.getSampleDouble(x + 1, y, j) * xdiff) + * (1 - ydiff) + + (src.getSampleDouble(x, y + 1, j) + * (1 - xdiff) + + src.getSampleDouble(x + 1, y + 1, j) + * xdiff) + * ydiff; + dst.setSample((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + j, result); + } + } + } + } + } + + /** + * Perform bicubic filtering + * based on http://local.wasp.uwa.edu.au/~pbourke/colour/bicubic/ + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterBicubic(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points on the destination raster, perform bicubic interpolation + // from corrosponding source points + double[] result = new double[src.getNumBands()]; + for (int i = 0; i < dpts.length; i += 2) + { + if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(), + (int) Math.round(pts[i + 1]) + src.getMinY())) + { + int x = (int) Math.floor(pts[i] + src.getMinX()); + int y = (int) Math.floor(pts[i + 1] + src.getMinY()); + double dx = pts[i] + src.getMinX() - x; + double dy = pts[i + 1] + src.getMinY() - y; + Arrays.fill(result, 0); + + for (int m = - 1; m < 3; m++) + { + for (int n = - 1; n < 3; n++) + { + // R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6 + double r1 = 0; + double r2 = 0; + + // Calculate R(m - dx) + double rx = m - dx + 2; + if (rx > 0) + r1 += rx * rx * rx; + + rx = m - dx + 1; + if (rx > 0) + r1 -= 4 * rx * rx * rx; + + rx = m - dx; + if (rx > 0) + r1 += 6 * rx * rx * rx; + + rx = m - dx - 1; + if (rx > 0) + r1 -= 4 * rx * rx * rx; + + r1 /= 6; + + // Calculate R(dy - n); + rx = dy - n + 2; + if (rx > 0) + r2 += rx * rx * rx; + + rx = dy - n + 1; + if (rx > 0) + r2 -= 4 * rx * rx * rx; + + rx = dy - n; + if (rx > 0) + r2 += 6 * rx * rx * rx; + + rx = dy - n - 1; + if (rx > 0) + r2 -= 4 * rx * rx * rx; + + r2 /= 6; + + // Calculate F(i+m, j+n) R(m - dx) R(dy - n) + // Check corner cases + int srcX = x + m; + if (srcX >= src.getMinX() + src.getWidth()) + srcX = src.getMinX() + src.getWidth() - 1; + else if (srcX < src.getMinX()) + srcX = src.getMinX(); + + int srcY = y + n; + if (srcY >= src.getMinY() + src.getHeight()) + srcY = src.getMinY() + src.getHeight() - 1; + else if (srcY < src.getMinY()) + srcY = src.getMinY(); + + // Calculate once for each band + for (int j = 0; j < result.length; j++) + result[j] += src.getSample(srcX, srcY, j) * r1 * r2; + } + } + + // Put it all together + for (int j = 0; j < result.length; j++) + dst.setSample((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + j, result[j]); + } + } + } } diff --git a/java/awt/image/BandCombineOp.java b/java/awt/image/BandCombineOp.java index 634125ed2..c4e2d5810 100644 --- a/java/awt/image/BandCombineOp.java +++ b/java/awt/image/BandCombineOp.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 Free Software Foundation +/* BandCombineOp.java - perform a combination on the bands of a raster + Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -36,7 +37,6 @@ exception statement from your version. */ package java.awt.image; -import java.awt.Point; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -65,52 +65,66 @@ public class BandCombineOp implements RasterOp * * @param matrix The matrix to filter pixels with. * @param hints Rendering hints to apply. Ignored. + * @throws ArrayIndexOutOfBoundsException if the matrix is invalid */ public BandCombineOp(float[][] matrix, RenderingHints hints) { - this.matrix = matrix; + this.matrix = new float[matrix.length][]; + int width = matrix[0].length; + for (int i = 0; i < matrix.length; i++) + { + this.matrix[i] = new float[width + 1]; + for (int j = 0; j < width; j++) + this.matrix[i][j] = matrix[i][j]; + + // The reference implementation pads the array with a trailing zero... + this.matrix[i][width] = 0; + } + this.hints = hints; } /** - * Filter Raster pixels through a matrix. - * - * Applies the Op matrix to source pixes to produce dest pixels. Each row - * of the matrix is multiplied by the src pixel components to produce the - * dest pixel. If matrix is one more than the number of bands in the src, - * the last element is implicitly multiplied by 1, i.e. added to the sum - * for that dest component. - * - * If dest is null, a suitable Raster is created. This implementation uses - * createCompatibleDestRaster. + * Filter Raster pixels through a matrix. Applies the Op matrix to source + * pixes to produce dest pixels. Each row of the matrix is multiplied by the + * src pixel components to produce the dest pixel. If matrix is one more than + * the number of bands in the src, the last element is implicitly multiplied + * by 1, i.e. added to the sum for that dest component. If dest is null, a + * suitable Raster is created. This implementation uses + * createCompatibleDestRaster. * * @param src The source Raster. - * @param dest The destination Raster, or null. - * @returns The destination Raster or an allocated Raster. + * @param dest The destination Raster, or null. + * @throws IllegalArgumentException if the destination raster is incompatible + * with the source raster. + * @return The filtered Raster. * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, - *java.awt.image.WritableRaster) + * java.awt.image.WritableRaster) */ public WritableRaster filter(Raster src, WritableRaster dest) { if (dest == null) dest = createCompatibleDestRaster(src); - + else if (dest.getNumBands() != src.getNumBands() + || dest.getTransferType() != src.getTransferType()) + throw new IllegalArgumentException("Destination raster is incompatible with source raster"); + // Filter the pixels - float[] spix = new float[matrix[0].length]; + float[] spix = new float[matrix[0].length - 1]; float[] dpix = new float[matrix.length]; for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) - { - // In case matrix rows have implicit translation - spix[spix.length - 1] = 1.0f; - src.getPixel(x, y, spix); - for (int i = 0; i < matrix.length; i++) { - dpix[i] = 0; - for (int j = 0; j < matrix[0].length; j++) - dpix[i] += spix[j] * matrix[i][j]; + // In case matrix rows have implicit translation + spix[spix.length - 1] = 1.0f; + src.getPixel(x, y, spix); + for (int i = 0; i < matrix.length; i++) + { + dpix[i] = 0; + for (int j = 0; j < matrix[0].length - 1; j++) + dpix[i] += spix[j] * matrix[i][j]; + } + dest.setPixel(x, y, dpix); } - dest.setPixel(x, y, dpix); - } return dest; } @@ -125,28 +139,48 @@ public class BandCombineOp implements RasterOp /** * Creates a new WritableRaster that can be used as the destination for this - * Op. This implementation creates a Banded Raster with data type FLOAT. - * @see - *java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + * Op. The number of bands in the source raster must equal the number of rows + * in the op matrix, which must also be equal to either the number of columns + * or (columns - 1) in the matrix. + * + * @param src The source raster. + * @return A compatible raster. + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + * @throws IllegalArgumentException if the raster is incompatible with the + * matrix. */ public WritableRaster createCompatibleDestRaster(Raster src) { - return Raster.createBandedRaster(DataBuffer.TYPE_FLOAT, src.getWidth(), - src.getHeight(), matrix.length, - new Point(src.getMinX(), src.getMinY())); + // Destination raster must have same number of bands as source + if (src.getNumBands() != matrix.length) + throw new IllegalArgumentException("Number of rows in matrix specifies an " + + "incompatible number of bands"); + + // We use -1 and -2 because we previously padded the rows with a trailing 0 + if (src.getNumBands() != matrix[0].length - 1 + && src.getNumBands() != matrix[0].length - 2) + throw new IllegalArgumentException("Incompatible number of bands: " + + "the number of bands in the raster must equal the number of " + + "columns in the matrix, optionally minus one"); + + return src.createCompatibleWritableRaster(); } - /** Return corresponding destination point for source point. + /** + * Return corresponding destination point for source point. Because this is + * not a geometric operation, it simply returns a copy of the source. * - * LookupOp will return the value of src unchanged. * @param src The source point. * @param dst The destination point. + * @return dst The destination point. * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, *java.awt.geom.Point2D) */ public final Point2D getPoint2D(Point2D src, Point2D dst) { - if (dst == null) return (Point2D)src.clone(); + if (dst == null) + return (Point2D)src.clone(); + dst.setLocation(src); return dst; } @@ -159,7 +193,11 @@ public class BandCombineOp implements RasterOp return hints; } - /** Return the matrix for this Op. */ + /** + * Return the matrix used in this operation. + * + * @return The matrix used in this operation. + */ public final float[][] getMatrix() { return matrix; diff --git a/java/awt/image/ColorConvertOp.java b/java/awt/image/ColorConvertOp.java index 1f85a5ecd..e6c85412d 100644 --- a/java/awt/image/ColorConvertOp.java +++ b/java/awt/image/ColorConvertOp.java @@ -38,7 +38,10 @@ exception statement from your version. */ package java.awt.image; +import gnu.java.awt.Buffers; + import java.awt.Graphics2D; +import java.awt.Point; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; @@ -47,9 +50,9 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** - * ColorConvertOp is a filter for converting an image from one colorspace to - * another colorspace. The filter can convert the image through a sequence - * of colorspaces or just from source to destination. + * ColorConvertOp is a filter for converting images or rasters between + * colorspaces, either through a sequence of colorspaces or just from source to + * destination. * * Color conversion is done on the color components without alpha. Thus * if a BufferedImage has alpha premultiplied, this is divided out before @@ -63,24 +66,22 @@ import java.awt.geom.Rectangle2D; */ public class ColorConvertOp implements BufferedImageOp, RasterOp { - private ColorSpace srccs; - private ColorSpace dstcs; private RenderingHints hints; - private ICC_Profile[] profiles; + private ICC_Profile[] profiles = null; private ColorSpace[] spaces; - private boolean rasterValid; /** - * Convert BufferedImage through a ColorSpace. + * Convert a BufferedImage through a ColorSpace. * - * This filter version is only valid for BufferedImages. The source image - * is converted to cspace. If the destination is not null, it is then - * converted to the destination colorspace. Normally this filter will only - * be used with a null destination. + * Objects created with this constructor can be used to convert + * BufferedImage's to a destination ColorSpace. Attempts to convert Rasters + * with this constructor will result in an IllegalArgumentException when the + * filter(Raster, WritableRaster) method is called. * * @param cspace The target color space. - * @param hints Rendering hints to use in conversion, or null. + * @param hints Rendering hints to use in conversion, if any (may be null) + * @throws NullPointerException if the ColorSpace is null. */ public ColorConvertOp(ColorSpace cspace, RenderingHints hints) { @@ -88,9 +89,27 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp throw new NullPointerException(); spaces = new ColorSpace[]{cspace}; this.hints = hints; - rasterValid = false; } + /** + * Convert from a source colorspace to a destination colorspace. + * + * This constructor takes two ColorSpace arguments as the source and + * destination color spaces. It is usually used with the + * filter(Raster, WritableRaster) method, in which case the source colorspace + * is assumed to correspond to the source Raster, and the destination + * colorspace with the destination Raster. + * + * If used with BufferedImages that do not match the source or destination + * colorspaces specified here, there is an implicit conversion from the + * source image to the source ColorSpace, or the destination ColorSpace to + * the destination image. + * + * @param srcCspace The source ColorSpace. + * @param dstCspace The destination ColorSpace. + * @param hints Rendering hints to use in conversion, if any (may be null). + * @throws NullPointerException if any ColorSpace is null. + */ public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, RenderingHints hints) { @@ -101,61 +120,77 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp } /** - * Convert from a source image destination image color space. + * Convert from a source colorspace to a destinatino colorspace. * * This constructor builds a ColorConvertOp from an array of ICC_Profiles. - * The source image will be converted through the sequence of color spaces + * The source will be converted through the sequence of color spaces * defined by the profiles. If the sequence of profiles doesn't give a - * well-defined conversion, throws IllegalArgumentException. - * - * NOTE: Sun's docs don't clearly define what a well-defined conversion is - * - or perhaps someone smarter can come along and sort it out. + * well-defined conversion, an IllegalArgumentException is thrown. * - * For BufferedImages, when the first and last profiles match the - * requirements of the source and destination color space respectively, the - * corresponding conversion is unnecessary. TODO: code this up. I don't - * yet understand how you determine this. + * If used with BufferedImages that do not match the source or destination + * colorspaces specified here, there is an implicit conversion from the + * source image to the source ColorSpace, or the destination ColorSpace to + * the destination image. * * For Rasters, the first and last profiles must have the same number of * bands as the source and destination Rasters, respectively. If this is * not the case, or there fewer than 2 profiles, an IllegalArgumentException * will be thrown. * - * @param profiles - * @param hints + * @param profiles An array of ICC_Profile's to convert through. + * @param hints Rendering hints to use in conversion, if any (may be null). + * @throws NullPointerException if the profile array is null. + * @throws IllegalArgumentException if the array is not a well-defined + * conversion. */ public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints) { if (profiles == null) throw new NullPointerException(); + this.hints = hints; this.profiles = profiles; - // TODO: Determine if this is well-defined. + // Create colorspace array with space for src and dest colorspace + // Note that the ICC_ColorSpace constructor will throw an + // IllegalArgumentException if the profile is invalid; thus we check + // for a "well defined conversion" spaces = new ColorSpace[profiles.length]; for (int i = 0; i < profiles.length; i++) spaces[i] = new ICC_ColorSpace(profiles[i]); } - /** Convert from source image color space to destination image color space. + /** + * Convert from source color space to destination color space. * * Only valid for BufferedImage objects, this Op converts from the source - * color space to the destination color space. The destination can't be - * null for this operation. + * image's color space to the destination image's color space. * - * @param hints Rendering hints to use during conversion, or null. + * The destination in the filter(BufferedImage, BufferedImage) method cannot + * be null for this operation, and it also cannot be used with the + * filter(Raster, WritableRaster) method. + * + * @param hints Rendering hints to use in conversion, if any (may be null). */ public ColorConvertOp(RenderingHints hints) { - this.hints = hints; - srccs = null; - dstcs = null; - rasterValid = false; + this.hints = hints; + spaces = new ColorSpace[0]; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, - java.awt.image.BufferedImage) + /** + * Converts the source image using the conversion path specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source and destination BufferedImage (if one is supplied) must have + * the same dimensions. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The transformed image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { @@ -163,129 +198,241 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp // For now we just suck it up and create intermediate buffers. if (dst == null && spaces.length == 0) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Not enough color space information " + + "to complete conversion."); + + if (dst != null + && (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth())) + throw new IllegalArgumentException("Source and destination images have " + + "different dimensions"); // Make sure input isn't premultiplied by alpha if (src.isAlphaPremultiplied()) - { - BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel()); - copyimage(src, tmp); - tmp.coerceData(false); - src = tmp; - } + { + BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel()); + copyimage(src, tmp); + tmp.coerceData(false); + src = tmp; + } - ColorModel scm = src.getColorModel(); + // Convert through defined intermediate conversions + BufferedImage tmp; for (int i = 0; i < spaces.length; i++) - { - BufferedImage tmp = createCompatibleDestImage(src, scm); - copyimage(src, tmp); - src = tmp; - } + { + if (src.getColorModel().getColorSpace().getType() != spaces[i].getType()) + { + tmp = createCompatibleDestImage(src, + createCompatibleColorModel(src, + spaces[i])); + copyimage(src, tmp); + src = tmp; + } + } - // Intermediate conversions leave result in src + // No implicit conversion to destination type needed; return result from the + // last intermediate conversions (which was left in src) if (dst == null) - return src; - - // Apply final conversion - copyimage(src, dst); - + dst = src; + + // Implicit conversion to destination image's color space + else + copyimage(src, dst); + return dst; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + /** + * Converts the source raster using the conversion path specified in the + * constructor. The resulting raster is stored in the destination raster if + * one is provided; otherwise a new WritableRaster is created and returned. + * + * This operation is not valid with every constructor of this class; see + * the constructors for details. Further, the source raster must have the + * same number of bands as the source ColorSpace, and the destination raster + * must have the same number of bands as the destination ColorSpace. + * + * The source and destination raster (if one is supplied) must also have the + * same dimensions. + * + * @param src The source raster. + * @param dest The destination raster. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The transformed raster. + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + // Various checks to ensure that the rasters and color spaces are compatible + if (spaces.length < 2) + throw new IllegalArgumentException("Not enough information about " + + "source and destination colorspaces."); + + if (spaces[0].getNumComponents() != src.getNumBands() + || (dest != null && spaces[spaces.length - 1].getNumComponents() != dest.getNumBands())) + throw new IllegalArgumentException("Source or destination raster " + + "contains the wrong number of bands."); + + if (dest != null + && (src.getHeight() != dest.getHeight() || src.getWidth() != dest.getWidth())) + throw new IllegalArgumentException("Source and destination rasters " + + "have different dimensions"); + + // Need to iterate through each color space. + // spaces[0] corresponds to the ColorSpace of the source raster, and + // spaces[spaces.length - 1] corresponds to the ColorSpace of the + // destination, with any number (or zero) of intermediate conversions. + + for (int i = 0; i < spaces.length - 2; i++) + { + WritableRaster tmp = createCompatibleDestRaster(src, spaces[i + 1], + false, + src.getTransferType()); + copyraster(src, spaces[i], tmp, spaces[i + 1]); + src = tmp; + } + + // The last conversion is done outside of the loop so that we can + // use the dest raster supplied, instead of creating our own temp raster + if (dest == null) + dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false, + DataBuffer.TYPE_BYTE); + copyraster(src, spaces[spaces.length - 2], dest, spaces[spaces.length - 1]); + + return dest; + } + + /** + * Creates an empty BufferedImage with the size equal to the source and the + * correct number of bands for the conversion defined in this Op. The newly + * created image is created with the specified ColorModel, or if no ColorModel + * is supplied, an appropriate one is chosen. + * + * @param src The source image. + * @param dstCM A color model for the destination image (may be null). + * @throws IllegalArgumentException if an appropriate colormodel cannot be + * chosen with the information given. + * @return The new compatible destination image. */ public BufferedImage createCompatibleDestImage(BufferedImage src, - ColorModel dstCM) + ColorModel dstCM) { - // FIXME: set properties to those in src + if (dstCM == null && spaces.length == 0) + throw new IllegalArgumentException("Don't know the destination " + + "colormodel"); + + if (dstCM == null) + { + dstCM = createCompatibleColorModel(src, spaces[spaces.length - 1]); + } + return new BufferedImage(dstCM, - src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, - null); + createCompatibleDestRaster(src.getRaster(), + dstCM.getColorSpace(), + src.getColorModel().hasAlpha, + dstCM.getTransferType()), + src.isPremultiplied, null); } - public final ICC_Profile[] getICC_Profiles() + /** + * Creates a new WritableRaster with the size equal to the source and the + * correct number of bands. + * + * Note, the new Raster will always use a BYTE storage size, regardless of + * the color model or defined destination; this is for compatibility with + * the reference implementation. + * + * @param src The source Raster. + * @throws IllegalArgumentException if there isn't enough colorspace + * information to create a compatible Raster. + * @return The new compatible destination raster. + */ + public WritableRaster createCompatibleDestRaster(Raster src) { - return profiles; - } + if (spaces.length < 2) + throw new IllegalArgumentException("Not enough destination colorspace " + + "information"); - /** Return the rendering hints for this op. */ - public final RenderingHints getRenderingHints() - { - return hints; + // Create a new raster with the last ColorSpace in the conversion + // chain, and with no alpha (implied) + return createCompatibleDestRaster(src, spaces[spaces.length-1], false, + DataBuffer.TYPE_BYTE); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + /** + * Returns the array of ICC_Profiles used to create this Op, or null if the + * Op was created using ColorSpace arguments. + * + * @return The array of ICC_Profiles, or null. */ - public final WritableRaster filter(Raster src, WritableRaster dest) + public final ICC_Profile[] getICC_Profiles() { - if (!rasterValid) - throw new IllegalArgumentException(); - - // Need to iterate through each color space - there must be at least 2 - for (int i = 1; i < spaces.length - 1; i++) - { - // FIXME: this is wrong. tmp needs to have the same number of bands as - // spaces[i] has. - WritableRaster tmp = createCompatibleDestRaster(src); - copyraster(src, spaces[i - 1], tmp, spaces[i]); - src = tmp; - } - - // FIXME: this is wrong. dst needs to have the same number of bands as - // spaces[i] has. - if (dest == null) - dest = createCompatibleDestRaster(src); - copyraster(src, spaces[spaces.length - 2], - dest, spaces[spaces.length - 1]); - - return dest; + return profiles; } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + /** + * Returns the rendering hints for this op. + * + * @return The rendering hints for this Op, or null. */ - public WritableRaster createCompatibleDestRaster(Raster src) + public final RenderingHints getRenderingHints() { - return src.createCompatibleWritableRaster(); + return hints; } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a source point. + * Because this is not a geometric operation, the destination and source + * points will be identical. * - * LookupOp will return the value of src unchanged. * @param src The source point. - * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + * @param dst The transformed destination point. + * @return The transformed destination point. */ public final Point2D getPoint2D(Point2D src, Point2D dst) { - if (dst == null) return (Point2D)src.clone(); + if (dst == null) + return (Point2D)src.clone(); + dst.setLocation(src); return dst; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + /** + * Returns the corresponding destination boundary of a source boundary. + * Because this is not a geometric operation, the destination and source + * boundaries will be identical. + * + * @param src The source boundary. + * @return The boundaries of the destination. */ public final Rectangle2D getBounds2D(BufferedImage src) { return src.getRaster().getBounds(); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + /** + * Returns the corresponding destination boundary of a source boundary. + * Because this is not a geometric operation, the destination and source + * boundaries will be identical. + * + * @param src The source boundary. + * @return The boundaries of the destination. */ public final Rectangle2D getBounds2D(Raster src) { return src.getBounds(); } - - // According to Sven de Marothy, we need to copy the src into the dest - // using Graphics2D, in order to use the rendering hints. + + /** + * Copy a source image to a destination image, respecting their colorspaces + * and performing colorspace conversions if necessary. + * + * @param src The source image. + * @param dst The destination image. + */ private void copyimage(BufferedImage src, BufferedImage dst) { + // This is done using Graphics2D in order to respect the rendering hints. Graphics2D gg = dst.createGraphics(); // If no hints are set there is no need to call @@ -297,13 +444,23 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp gg.dispose(); } - private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, - ColorSpace dcs) + /** + * Copy a source raster to a destination raster, performing a colorspace + * conversion between the two. The conversion will respect the + * KEY_COLOR_RENDERING rendering hint if one is present. + * + * @param src The source raster. + * @param scs The colorspace of the source raster. + * @dst The destination raster. + * @dcs The colorspace of the destination raster. + */ + private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, ColorSpace dcs) { float[] sbuf = new float[src.getNumBands()]; - if (hints.get(RenderingHints.KEY_COLOR_RENDERING) == - RenderingHints.VALUE_COLOR_RENDER_QUALITY) + if (hints != null + && hints.get(RenderingHints.KEY_COLOR_RENDERING) == + RenderingHints.VALUE_COLOR_RENDER_QUALITY) { // use cie for accuracy for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) @@ -321,4 +478,60 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp } } + /** + * This method creates a color model with the same colorspace and alpha + * settings as the source image. The created color model will always be a + * ComponentColorModel and have a BYTE transfer type. + * + * @param img The source image. + * @param cs The ColorSpace to use. + * @return A color model compatible with the source image. + */ + private ColorModel createCompatibleColorModel(BufferedImage img, ColorSpace cs) + { + // The choice of ComponentColorModel and DataBuffer.TYPE_BYTE is based on + // Mauve testing of the reference implementation. + return new ComponentColorModel(cs, + img.getColorModel().hasAlpha(), + img.isAlphaPremultiplied(), + img.getColorModel().getTransparency(), + DataBuffer.TYPE_BYTE); + } + + /** + * This method creates a compatible Raster, given a source raster, colorspace, + * alpha value, and transfer type. + * + * @param src The source raster. + * @param cs The ColorSpace to use. + * @param hasAlpha Whether the raster should include a component for an alpha. + * @param transferType The size of a single data element. + * @return A compatible WritableRaster. + */ + private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs, + boolean hasAlpha, + int transferType) + { + // The use of a PixelInterleavedSampleModel weas determined using mauve + // tests, based on the reference implementation + + int numComponents = cs.getNumComponents(); + if (hasAlpha) + numComponents++; + + int[] offsets = new int[numComponents]; + for (int i = 0; i < offsets.length; i++) + offsets[i] = i; + + DataBuffer db = Buffers.createBuffer(transferType, + src.getWidth() * src.getHeight() * numComponents, + 1); + return new WritableRaster(new PixelInterleavedSampleModel(transferType, + src.getWidth(), + src.getHeight(), + numComponents, + numComponents * src.getWidth(), + offsets), + db, new Point(src.getMinX(), src.getMinY())); + } } diff --git a/java/awt/image/ColorModel.java b/java/awt/image/ColorModel.java index 9e559db37..fc413d0b4 100644 --- a/java/awt/image/ColorModel.java +++ b/java/awt/image/ColorModel.java @@ -628,7 +628,7 @@ public abstract class ColorModel implements Transparency public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { - if (this.isAlphaPremultiplied == isAlphaPremultiplied) + if (this.isAlphaPremultiplied == isAlphaPremultiplied || ! hasAlpha) return this; int w = raster.getWidth(); diff --git a/java/awt/image/ComponentColorModel.java b/java/awt/image/ComponentColorModel.java index f56688f93..f3f3e374b 100644 --- a/java/awt/image/ComponentColorModel.java +++ b/java/awt/image/ComponentColorModel.java @@ -42,9 +42,11 @@ import gnu.java.awt.Buffers; import java.awt.Point; import java.awt.color.ColorSpace; +import java.util.Arrays; public class ComponentColorModel extends ColorModel { + // Find sum of all elements of the array. private static int sum(int[] values) { int sum = 0; @@ -52,6 +54,22 @@ public class ComponentColorModel extends ColorModel sum += values[i]; return sum; } + + // Create an appropriate array of bits, given a colorspace (ie, number of + // bands), size of the storage data type, and presence of an alpha band. + private static int[] findBits(ColorSpace colorSpace, int transferType, + boolean hasAlpha) + { + int[] bits; + if (hasAlpha) + bits = new int[colorSpace.getNumComponents()+1]; + else + bits = new int[colorSpace.getNumComponents()]; + + Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType)); + + return bits; + } public ComponentColorModel(ColorSpace colorSpace, int[] bits, boolean hasAlpha, @@ -84,8 +102,8 @@ public class ComponentColorModel extends ColorModel boolean isAlphaPremultiplied, int transparency, int transferType) { - this(colorSpace, null, hasAlpha, isAlphaPremultiplied, - transparency, transferType); + this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha, + isAlphaPremultiplied, transparency, transferType); } public int getRed(int pixel) diff --git a/java/awt/image/ConvolveOp.java b/java/awt/image/ConvolveOp.java index ffb834874..cd3b01131 100644 --- a/java/awt/image/ConvolveOp.java +++ b/java/awt/image/ConvolveOp.java @@ -38,7 +38,6 @@ exception statement from your version. */ package java.awt.image; -import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -51,11 +50,13 @@ import java.awt.geom.Rectangle2D; * with elements in the kernel to compute a new pixel. * * Each band in a Raster is convolved and copied to the destination Raster. + * For BufferedImages, convolution is applied to all components. Color + * conversion will be applied if needed. * - * For BufferedImages, convolution is applied to all components. If the - * source is not premultiplied, the data will be premultiplied before - * convolving. Premultiplication will be undone if the destination is not - * premultiplied. Color conversion will be applied if needed. + * Note that this filter ignores whether the source or destination is alpha + * premultiplied. The reference spec states that data will be premultiplied + * prior to convolving and divided back out afterwards (if needed), but testing + * has shown that this is not the case with their implementation. * * @author jlquinn@optonline.net */ @@ -104,59 +105,83 @@ public class ConvolveOp implements BufferedImageOp, RasterOp hints = null; } - - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, - * java.awt.image.BufferedImage) + /** + * Converts the source image using the kernel specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source and destination BufferedImage (if one is supplied) must have + * the same dimensions. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The convolved image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { if (src == dst) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Source and destination images " + + "cannot be the same."); if (dst == null) dst = createCompatibleDestImage(src, src.getColorModel()); // Make sure source image is premultiplied BufferedImage src1 = src; - if (!src.isPremultiplied) + // The spec says we should do this, but mauve testing shows that Sun's + // implementation does not check this. + /* + if (!src.isAlphaPremultiplied()) { src1 = createCompatibleDestImage(src, src.getColorModel()); src.copyData(src1.getRaster()); src1.coerceData(true); } + */ BufferedImage dst1 = dst; - if (!src.getColorModel().equals(dst.getColorModel())) + if (src1.getColorModel().getColorSpace().getType() != dst.getColorModel().getColorSpace().getType()) dst1 = createCompatibleDestImage(src, src.getColorModel()); filter(src1.getRaster(), dst1.getRaster()); + // Since we don't coerceData above, we don't need to divide it back out. + // This is wrong (one mauve test specifically tests converting a non- + // premultiplied image to a premultiplied image, and it shows that Sun + // simply ignores the premultipled flag, contrary to the spec), but we + // mimic it for compatibility. + /* + if (! dst.isAlphaPremultiplied()) + dst1.coerceData(false); + */ + + // Convert between color models if needed if (dst1 != dst) - { - // Convert between color models. - // TODO Check that premultiplied alpha is handled correctly here. - Graphics2D gg = dst.createGraphics(); - gg.setRenderingHints(hints); - gg.drawImage(dst1, 0, 0, null); - gg.dispose(); - } - + new ColorConvertOp(hints).filter(dst1, dst); + return dst; } - /* (non-Javadoc) - * @see - * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, - * java.awt.image.ColorModel) + /** + * Creates an empty BufferedImage with the size equal to the source and the + * correct number of bands. The new image is created with the specified + * ColorModel, or if no ColorModel is supplied, an appropriate one is chosen. + * + * @param src The source image. + * @param dstCM A color model for the destination image (may be null). + * @return The new compatible destination image. */ public BufferedImage createCompatibleDestImage(BufferedImage src, - ColorModel dstCM) + ColorModel dstCM) { - // FIXME: set properties to those in src - return new BufferedImage(dstCM, - src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, null); + if (dstCM != null) + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); + + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); } /* (non-Javadoc) @@ -168,6 +193,8 @@ public class ConvolveOp implements BufferedImageOp, RasterOp } /** + * Get the edge condition for this Op. + * * @return The edge condition. */ public int getEdgeCondition() @@ -185,9 +212,22 @@ public class ConvolveOp implements BufferedImageOp, RasterOp return (Kernel) kernel.clone(); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, - * java.awt.image.WritableRaster) + /** + * Converts the source raster using the kernel specified in the constructor. + * The resulting raster is stored in the destination raster if one is + * provided; otherwise a new WritableRaster is created and returned. + * + * If the convolved value for a sample is outside the range of [0-255], it + * will be clipped. + * + * The source and destination raster (if one is supplied) cannot be the same, + * and must also have the same dimensions. + * + * @param src The source raster. + * @param dest The destination raster. + * @throws IllegalArgumentException if the rasters identical. + * @throws ImagingOpException if the convolution is not possible. + * @return The transformed raster. */ public final WritableRaster filter(Raster src, WritableRaster dest) { @@ -228,7 +268,16 @@ public class ConvolveOp implements BufferedImageOp, RasterOp 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. + // out why this is necessary. + + // This clipping is pretty strange, and seems to be hard-coded + // at 255 (regardless of the raster's datatype or transfertype). + // But it's what the reference does. + if (v > 255) + v = 255; + if (v < 0) + v = 0; + dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), b, v); } @@ -310,13 +359,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp return src.getBounds(); } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a source point. Because + * this is not a geometric operation, the destination and source points will + * be identical. * - * ConvolveOp will return the value of src unchanged. * @param src The source point. - * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, - * java.awt.geom.Point2D) + * @param dst The transformed destination point. + * @return The transformed destination point. */ public final Point2D getPoint2D(Point2D src, Point2D dst) { diff --git a/java/io/File.java b/java/io/File.java index 7dbc6109e..4f5ab4e66 100644 --- a/java/io/File.java +++ b/java/io/File.java @@ -286,7 +286,8 @@ public class File implements Serializable, Comparable<File> // example, is a valid and minimal path). if (plen > 1 && p.charAt (plen - 1) == separatorChar) { - if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')) + if (! (separatorChar == '\\' && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar)))) return p.substring (0, plen - 1); } else @@ -303,7 +304,16 @@ public class File implements Serializable, Comparable<File> { dupIndex++; if (dupIndex == plen) - return newpath.toString(); + { + if ((separatorChar == '\\' + && newpath.length() == 2 + && newpath.charAt(1) == ':') + || (separatorChar != '\\' && newpath.length() == 0)) + { + newpath.append(separatorChar); + } + return newpath.toString(); + } } newpath.append(separatorChar); last = dupIndex; @@ -315,7 +325,9 @@ public class File implements Serializable, Comparable<File> int end; if (plen > 1 && p.charAt (plen - 1) == separatorChar) { - if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':') + if (separatorChar == '\\' + && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar))) end = plen; else end = plen - 1; @@ -427,45 +439,8 @@ public class File implements Serializable, Comparable<File> { if (isAbsolute()) return path; - else if (separatorChar == '\\' - && path.length() > 0 && path.charAt (0) == '\\') - { - // On Windows, even if the path starts with a '\\' it is not - // really absolute until we prefix the drive specifier from - // the current working directory to it. - return System.getProperty ("user.dir").substring (0, 2) + path; - } - else if (separatorChar == '\\' - && path.length() > 1 && path.charAt (1) == ':' - && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') - || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))) - { - // On Windows, a process has a current working directory for - // each drive and a path like "G:foo\bar" would mean the - // absolute path "G:\wombat\foo\bar" if "\wombat" is the - // working directory on the G drive. - String drvDir = null; - try - { - drvDir = new File (path.substring (0, 2)).getCanonicalPath(); - } - catch (IOException e) - { - drvDir = path.substring (0, 2) + "\\"; - } - - // Note: this would return "C:\\." for the path "C:.", if "\" - // is the working folder on the C drive, but this is - // consistent with what Sun's JRE 1.4.1.01 actually returns! - if (path.length() > 2) - return drvDir + '\\' + path.substring (2, path.length()); - else - return drvDir; - } - else if (path.equals("")) - return System.getProperty ("user.dir"); else - return System.getProperty ("user.dir") + separatorChar + path; + return VMFile.getAbsolutePath(path); } /** @@ -657,15 +632,7 @@ public class File implements Serializable, Comparable<File> */ public boolean isAbsolute() { - if (separatorChar == '\\') - return path.startsWith(dupSeparator) || - (path.length() > 2 && - ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || - (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && - path.charAt(1) == ':' && - path.charAt(2) == '\\'); - else - return path.startsWith(separator); + return VMFile.isAbsolute(path); } /** @@ -998,14 +965,7 @@ public class File implements Serializable, Comparable<File> */ public URL toURL() throws MalformedURLException { - // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt", - // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". - if (separatorChar == '\\') - return new URL ("file:/" + getAbsolutePath().replace ('\\', '/') - + (isDirectory() ? "/" : "")); - else - return new URL ("file:" + getAbsolutePath() - + (isDirectory() ? "/" : "")); + return VMFile.toURL(this); } diff --git a/java/io/FileDescriptor.java b/java/io/FileDescriptor.java index d300c9cb6..dd3db1c74 100644 --- a/java/io/FileDescriptor.java +++ b/java/io/FileDescriptor.java @@ -133,7 +133,8 @@ public final class FileDescriptor * native file handle, <code>false</code> otherwise */ public boolean valid () - { - return channel != null && channel.isOpen(); + { + ByteChannel c = channel; + return (c != null) && (c.isOpen()); } } diff --git a/java/io/InputStreamReader.java b/java/io/InputStreamReader.java index 936a03c95..8d97799d5 100644 --- a/java/io/InputStreamReader.java +++ b/java/io/InputStreamReader.java @@ -135,6 +135,16 @@ public class InputStreamReader extends Reader private boolean hasSavedSurrogate = false; /** + * A byte array to be reused in read(byte[], int, int). + */ + private byte[] bytesCache; + + /** + * Locks the bytesCache above in read(byte[], int, int). + */ + private Object cacheLock = new Object(); + + /** * This method initializes a new instance of <code>InputStreamReader</code> * to read from the specified stream using the default encoding. * @@ -355,9 +365,19 @@ public class InputStreamReader extends Reader throw new IOException("Reader has been closed"); if (isDone) return -1; - if(decoder != null){ - int totalBytes = (int)((double)length * maxBytesPerChar); - byte[] bytes = new byte[totalBytes]; + if(decoder != null) + { + int totalBytes = (int)((double) length * maxBytesPerChar); + byte[] bytes; + // Fetch cached bytes array if available and big enough. + synchronized(cacheLock) + { + bytes = bytesCache; + if (bytes == null || bytes.length < totalBytes) + bytes = new byte[totalBytes]; + else + bytesCache = null; + } int remaining = 0; if(byteBuffer != null) @@ -410,12 +430,40 @@ public class InputStreamReader extends Reader byteBuffer = null; read = cb.position() - startPos; - return (read <= 0) ? -1 : read; - } else { - byte[] bytes = new byte[length]; + + // Put cached bytes array back if we are finished and the cache + // is null or smaller than the used bytes array. + synchronized (cacheLock) + { + if (byteBuffer == null + && (bytesCache == null || bytesCache.length < bytes.length)) + bytesCache = bytes; + } + return (read <= 0) ? -1 : read; + } + else + { + byte[] bytes; + // Fetch cached bytes array if available and big enough. + synchronized (cacheLock) + { + bytes = bytesCache; + if (bytes == null || length < bytes.length) + bytes = new byte[length]; + else + bytesCache = null; + } + int read = in.read(bytes); for(int i=0;i<read;i++) buf[offset+i] = (char)(bytes[i]&0xFF); + + // Put back byte array into cache if appropriate. + synchronized (cacheLock) + { + if (bytesCache == null || bytesCache.length < bytes.length) + bytesCache = bytes; + } return read; } } diff --git a/java/io/ObjectInputStream.java b/java/io/ObjectInputStream.java index 98632607c..d6c1406ea 100644 --- a/java/io/ObjectInputStream.java +++ b/java/io/ObjectInputStream.java @@ -39,7 +39,7 @@ exception statement from your version. */ package java.io; -import gnu.java.io.ObjectIdentityWrapper; +import gnu.classpath.VMStackWalker; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -104,7 +104,7 @@ public class ObjectInputStream extends InputStream this.blockDataInput = new DataInputStream(this); this.realInputStream = new DataInputStream(in); this.nextOID = baseWireHandle; - this.objectLookupTable = new Hashtable<Integer,ObjectIdentityWrapper>(); + this.objectLookupTable = new Vector<Object>(); this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); setBlockDataMode(true); readStreamHeader(); @@ -204,10 +204,9 @@ public class ObjectInputStream extends InputStream case TC_REFERENCE: { if(dump) dumpElement("REFERENCE "); - Integer oid = new Integer(this.realInputStream.readInt()); - if(dump) dumpElementln(Integer.toHexString(oid.intValue())); - ret_val = ((ObjectIdentityWrapper) - this.objectLookupTable.get(oid)).object; + int oid = realInputStream.readInt(); + if(dump) dumpElementln(Integer.toHexString(oid)); + ret_val = lookupHandle(oid); break; } @@ -360,8 +359,7 @@ public class ObjectInputStream extends InputStream this.currentObject = obj; this.currentObjectValidators = null; - ObjectStreamClass[] hierarchy = - inputGetObjectStreamClasses(clazz); + ObjectStreamClass[] hierarchy = hierarchy(clazz); for (int i = 0; i < hierarchy.length; i++) { @@ -825,7 +823,7 @@ public class ObjectInputStream extends InputStream */ private ClassLoader currentLoader() { - return VMObjectInputStream.currentClassLoader(); + return VMStackWalker.firstNonNullClassLoader(); } /** @@ -853,41 +851,20 @@ public class ObjectInputStream extends InputStream } /** - * Reconstruct class hierarchy the same way - * {@link java.io.ObjectStreamClass#getObjectStreamClasses(Class)} does - * but using lookupClass instead of ObjectStreamClass.lookup. This - * dup is necessary localize the lookup table. Hopefully some future - * rewritings will be able to prevent this. + * Reconstruct class hierarchy the same way {@link + * java.io.ObjectStreamClass#hierarchy} does but using lookupClass + * instead of ObjectStreamClass.lookup. * * @param clazz This is the class for which we want the hierarchy. * * @return An array of valid {@link java.io.ObjectStreamClass} instances which * represent the class hierarchy for clazz. */ - private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz) - { + private ObjectStreamClass[] hierarchy(Class clazz) + { ObjectStreamClass osc = lookupClass(clazz); - if (osc == null) - return new ObjectStreamClass[0]; - else - { - Vector<ObjectStreamClass> oscs = new Vector<ObjectStreamClass>(); - - while (osc != null) - { - oscs.addElement(osc); - osc = osc.getSuper(); - } - - int count = oscs.size(); - ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count]; - - for (int i = count - 1; i >= 0; i--) - sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i); - - return sorted_oscs; - } + return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); } /** @@ -1571,9 +1548,47 @@ public class ObjectInputStream extends InputStream */ private int assignNewHandle(Object obj) { - this.objectLookupTable.put(new Integer(this.nextOID), - new ObjectIdentityWrapper(obj)); - return this.nextOID++; + int handle = this.nextOID; + this.nextOID = handle + 1; + rememberHandle(obj,handle); + return handle; + } + + /** + * Remember the object associated with the given handle. + * + * @param obj an object + * + * @param handle a handle, must be >= baseWireHandle + * + * @see #lookupHandle + */ + private void rememberHandle(Object obj, int handle) + { + Vector olt = this.objectLookupTable; + handle = handle - baseWireHandle; + + if (olt.size() <= handle) + olt.setSize(handle + 1); + + olt.set(handle, obj); + } + + /** + * Look up the object associated with a given handle. + * + * @param handle a handle, must be >= baseWireHandle + * + * @return the object remembered for handle or null if none. + * + * @see #rememberHandle + */ + private Object lookupHandle(int handle) + { + Vector olt = this.objectLookupTable; + handle = handle - baseWireHandle; + Object result = handle < olt.size() ? olt.get(handle) : null; + return result; } private Object processResolution(ObjectStreamClass osc, Object obj, int handle) @@ -1607,9 +1622,7 @@ public class ObjectInputStream extends InputStream if (this.resolveEnabled) obj = resolveObject(obj); - this.objectLookupTable.put(new Integer(handle), - new ObjectIdentityWrapper(obj)); - + rememberHandle(obj, handle); return obj; } @@ -1942,7 +1955,7 @@ public class ObjectInputStream extends InputStream private boolean useSubclassMethod; private int nextOID; private boolean resolveEnabled; - private Hashtable<Integer,ObjectIdentityWrapper> objectLookupTable; + private Vector<Object> objectLookupTable; private Object currentObject; private ObjectStreamClass currentObjectStreamClass; private TreeSet<ValidatorAndPriority> currentObjectValidators; diff --git a/java/io/ObjectOutputStream.java b/java/io/ObjectOutputStream.java index eb781acab..c3c3df9a3 100644 --- a/java/io/ObjectOutputStream.java +++ b/java/io/ObjectOutputStream.java @@ -39,7 +39,7 @@ exception statement from your version. */ package java.io; -import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.io.ObjectIdentityMap2Int; import gnu.java.lang.reflect.TypeSignature; import gnu.java.security.action.SetAccessibleAction; @@ -47,8 +47,6 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.security.AccessController; -import java.util.Hashtable; /** @@ -146,7 +144,7 @@ public class ObjectOutputStream extends OutputStream replacementEnabled = false; isSerializing = false; nextOID = baseWireHandle; - OIDLookupTable = new Hashtable<ObjectIdentityWrapper,Integer>(); + OIDLookupTable = new ObjectIdentityMap2Int(); protocolVersion = defaultProtocolVersion; useSubclassMethod = false; writeStreamHeader(); @@ -213,11 +211,11 @@ public class ObjectOutputStream extends OutputStream break; } - Integer handle = findHandle(obj); - if (handle != null) + int handle = findHandle(obj); + if (handle >= 0) { realOutput.writeByte(TC_REFERENCE); - realOutput.writeInt(handle.intValue()); + realOutput.writeInt(handle); break; } @@ -231,7 +229,7 @@ public class ObjectOutputStream extends OutputStream writeObject (osc); } else - { + {System.err.println("1"); realOutput.writeByte(TC_PROXYCLASSDESC); Class[] intfs = cl.getInterfaces(); realOutput.writeInt(intfs.length); @@ -344,8 +342,7 @@ public class ObjectOutputStream extends OutputStream Object prevObject = this.currentObject; ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; currentObject = obj; - ObjectStreamClass[] hierarchy = - ObjectStreamClass.getObjectStreamClasses(clazz); + ObjectStreamClass[] hierarchy = osc.hierarchy(); for (int i = 0; i < hierarchy.length; i++) { @@ -1110,17 +1107,16 @@ public class ObjectOutputStream extends OutputStream // lookup the handle for OBJ, return null if OBJ doesn't have a // handle yet - private Integer findHandle(Object obj) + private int findHandle(Object obj) { - return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj)); + return OIDLookupTable.get(obj); } // assigns the next availible handle to OBJ private int assignNewHandle(Object obj) { - OIDLookupTable.put(new ObjectIdentityWrapper(obj), - new Integer(nextOID)); + OIDLookupTable.put(obj, nextOID); return nextOID++; } @@ -1222,39 +1218,70 @@ public class ObjectOutputStream extends OutputStream { ObjectStreamField[] fields = osc.fields; boolean oldmode = setBlockDataMode(false); - String field_name; - Class type; - for (int i = 0; i < fields.length; i++) + try { - field_name = fields[i].getName(); - type = fields[i].getType(); - - if (dump) - dumpElementln ("WRITE FIELD: " + field_name + " type=" + type); - - if (type == Boolean.TYPE) - realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name)); - else if (type == Byte.TYPE) - realOutput.writeByte(getByteField(obj, osc.forClass(), field_name)); - else if (type == Character.TYPE) - realOutput.writeChar(getCharField(obj, osc.forClass(), field_name)); - else if (type == Double.TYPE) - realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name)); - else if (type == Float.TYPE) - realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name)); - else if (type == Integer.TYPE) - realOutput.writeInt(getIntField(obj, osc.forClass(), field_name)); - else if (type == Long.TYPE) - realOutput.writeLong(getLongField(obj, osc.forClass(), field_name)); - else if (type == Short.TYPE) - realOutput.writeShort(getShortField(obj, osc.forClass(), field_name)); - else - writeObject(getObjectField(obj, osc.forClass(), field_name, - fields[i].getTypeString ())); + writeFields(obj,fields); + } + catch (IllegalArgumentException _) + { + InvalidClassException e = new InvalidClassException + ("writing fields of class " + osc.forClass().getName()); + e.initCause(_); + throw e; + } + catch (IOException e) + { + throw e; } + catch (Exception _) + { + IOException e = new IOException("Unexpected exception " + _); + e.initCause(_); + throw(e); + } + setBlockDataMode(oldmode); } + + + /** + * Helper function for writeFields(Object,ObjectStreamClass): write + * fields from given fields array. Pass exception on. + * + * @param obj the object to be written + * + * @param fields the fields of obj to be written. + */ + private void writeFields(Object obj, ObjectStreamField[] fields) + throws + IllegalArgumentException, IllegalAccessException, IOException + { + for (int i = 0; i < fields.length; i++) + { + ObjectStreamField osf = fields[i]; + Field field = osf.field; + + if (DEBUG && dump) + dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType()); + + switch (osf.getTypeCode()) + { + case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break; + case 'B': realOutput.writeByte (field.getByte (obj)); break; + case 'S': realOutput.writeShort (field.getShort (obj)); break; + case 'C': realOutput.writeChar (field.getChar (obj)); break; + case 'I': realOutput.writeInt (field.getInt (obj)); break; + case 'F': realOutput.writeFloat (field.getFloat (obj)); break; + case 'J': realOutput.writeLong (field.getLong (obj)); break; + case 'D': realOutput.writeDouble (field.getDouble (obj)); break; + case 'L': + case '[': writeObject (field.get (obj)); break; + default: + throw new IOException("Unexpected type code " + osf.getTypeCode()); + } + } + } // Toggles writing primitive data to block-data buffer. @@ -1313,248 +1340,6 @@ public class ObjectOutputStream extends OutputStream } } - private boolean getBooleanField(Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField(klass, field_name); - boolean b = f.getBoolean(obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private byte getByteField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - byte b = f.getByte (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private char getCharField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - char b = f.getChar (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private double getDoubleField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - double b = f.getDouble (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private float getFloatField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - float b = f.getFloat (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private int getIntField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - int b = f.getInt (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private long getLongField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - long b = f.getLong (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private short getShortField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - short b = f.getShort (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private Object getObjectField (Object obj, Class klass, String field_name, - String type_code) throws IOException - { - try - { - Field f = getField (klass, field_name); - ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType()); - - /* if of is primitive something went wrong - * in the check for primitive classes in writeFields. - */ - if (of.isPrimitive()) - throw new InvalidClassException - ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive"); - - if (!of.getTypeString().equals(type_code)) - throw new InvalidClassException - ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code); - - Object o = f.get (obj); - // FIXME: We should check the type_code here - return o; - } - catch (IOException e) - { - throw e; - } - catch (Exception e) - { - throw new IOException (); - } - } - - private Field getField (Class klass, String name) - throws java.io.InvalidClassException - { - try - { - final Field f = klass.getDeclaredField(name); - setAccessible.setMember(f); - AccessController.doPrivileged(setAccessible); - return f; - } - catch (java.lang.NoSuchFieldException e) - { - throw new InvalidClassException - ("no field called " + name + " in class " + klass.getName()); - } - } - private void dumpElementln (String msg) { for (int i = 0; i < depth; i++) @@ -1582,7 +1367,7 @@ public class ObjectOutputStream extends OutputStream private boolean replacementEnabled; private boolean isSerializing; private int nextOID; - private Hashtable<ObjectIdentityWrapper,Integer> OIDLookupTable; + private ObjectIdentityMap2Int OIDLookupTable; private int protocolVersion; private boolean useSubclassMethod; private SetAccessibleAction setAccessible = new SetAccessibleAction(); diff --git a/java/io/ObjectStreamClass.java b/java/io/ObjectStreamClass.java index 1ca78ed9f..21a80c392 100644 --- a/java/io/ObjectStreamClass.java +++ b/java/io/ObjectStreamClass.java @@ -59,7 +59,6 @@ import java.security.Security; import java.util.Arrays; import java.util.Comparator; import java.util.Hashtable; -import java.util.Vector; /** * @author Tom Tromey (tromey@redhat.com) @@ -242,37 +241,45 @@ public class ObjectStreamClass implements Serializable return superClass; } - - // returns an array of ObjectStreamClasses that represent the super - // classes of CLAZZ and CLAZZ itself in order from most super to - // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ - // that is serializable. - static ObjectStreamClass[] getObjectStreamClasses(Class<?> clazz) + /** + * returns an array of ObjectStreamClasses that represent the super + * classes of the class represented by this and the class + * represented by this itself in order from most super to this. + * ObjectStreamClass[0] is the highest superclass of this that is + * serializable. + * + * The result of consecutive calls this hierarchy() will be the same + * array instance. + * + * @return an array of ObjectStreamClass representing the + * super-class hierarchy of serializable classes. + */ + ObjectStreamClass[] hierarchy() { - ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); - - if (osc == null) - return new ObjectStreamClass[0]; - else - { - Vector<ObjectStreamClass> oscs = new Vector<ObjectStreamClass>(); - - while (osc != null) - { - oscs.addElement (osc); - osc = osc.getSuper(); - } - - int count = oscs.size(); - ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; - - for (int i = count - 1; i >= 0; i--) - sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); - - return sorted_oscs; + ObjectStreamClass[] result = hierarchy; + if (result == null) + { + int d = 0; + + for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + d++; + + result = new ObjectStreamClass[d]; + + for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + { + result[--d] = osc; + } + + hierarchy = result; } + return result; } + /** + * Cache for hierarchy() result. + */ + private ObjectStreamClass[] hierarchy = null; // Returns an integer that consists of bit-flags that indicate // properties of the class represented by this ObjectStreamClass. @@ -305,7 +312,7 @@ public class ObjectStreamClass implements Serializable * already set UID is found. */ void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException - { + {hierarchy = null; this.clazz = cl; cacheMethods(); @@ -432,6 +439,7 @@ public class ObjectStreamClass implements Serializable void setSuperclass (ObjectStreamClass osc) { superClass = osc; + hierarchy = null; } void calculateOffsets() @@ -554,21 +562,62 @@ outer: return null; } + /** + * Helper routine to check if a class was loaded by boot or + * application class loader. Classes for which this is not the case + * should not be cached since caching prevent class file garbage + * collection. + * + * @param cl a class + * + * @return true if cl was loaded by boot or application class loader, + * false if cl was loaded by a user class loader. + */ + private static boolean loadedByBootOrApplicationClassLoader(Class cl) + { + ClassLoader l = cl.getClassLoader(); + return + ( l == null /* boot loader */ ) + || (l == ClassLoader.getSystemClassLoader() /* application loader */); + } + + static Hashtable methodCache = new Hashtable(); + + static final Class[] readObjectSignature = { ObjectInputStream.class }; + static final Class[] writeObjectSignature = { ObjectOutputStream.class }; + private void cacheMethods() { - Method[] methods = forClass().getDeclaredMethods(); - - readObjectMethod = findMethod(methods, "readObject", - new Class[] { ObjectInputStream.class }, - Void.TYPE, true); - writeObjectMethod = findMethod(methods, "writeObject", - new Class[] { ObjectOutputStream.class }, - Void.TYPE, true); - - // readResolve and writeReplace can be in parent classes, as long as they - // are accessible from this class. - readResolveMethod = findAccessibleMethod("readResolve", forClass()); - writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); + Class cl = forClass(); + Method[] cached = (Method[]) methodCache.get(cl); + if (cached == null) + { + cached = new Method[4]; + Method[] methods = cl.getDeclaredMethods(); + + cached[0] = findMethod(methods, "readObject", + readObjectSignature, + Void.TYPE, true); + cached[1] = findMethod(methods, "writeObject", + writeObjectSignature, + Void.TYPE, true); + + // readResolve and writeReplace can be in parent classes, as long as they + // are accessible from this class. + cached[2] = findAccessibleMethod("readResolve", cl); + cached[3] = findAccessibleMethod("writeReplace", cl); + + /* put in cache if classes not loaded by user class loader. + * For a user class loader, the cache may otherwise grow + * without limit. + */ + if (loadedByBootOrApplicationClassLoader(cl)) + methodCache.put(cl,cached); + } + readObjectMethod = cached[0]; + writeObjectMethod = cached[1]; + readResolveMethod = cached[2]; + writeReplaceMethod = cached[3]; } private ObjectStreamClass(Class cl) @@ -720,152 +769,208 @@ outer: calculateOffsets(); } + static Hashtable uidCache = new Hashtable(); + // Returns the serial version UID defined by class, or if that // isn't present, calculates value of serial version UID. private long getClassUID(Class cl) { - try + long result = 0; + Long cache = (Long) uidCache.get(cl); + if (cache != null) + result = cache.longValue(); + else { - // Use getDeclaredField rather than getField, since serialVersionUID - // may not be public AND we only want the serialVersionUID of this - // class, not a superclass or interface. - final Field suid = cl.getDeclaredField("serialVersionUID"); - SetAccessibleAction setAccessible = new SetAccessibleAction(suid); - AccessController.doPrivileged(setAccessible); - int modifiers = suid.getModifiers(); - - if (Modifier.isStatic(modifiers) - && Modifier.isFinal(modifiers) - && suid.getType() == Long.TYPE) - return suid.getLong(null); + try + { + result = getClassUIDFromField(cl); + } + catch (NoSuchFieldException ignore) + { + try + { + result = calculateClassUID(cl); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException + ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName(), e); + } + catch (IOException ioe) + { + throw new RuntimeException(ioe); + } + } + + if (loadedByBootOrApplicationClassLoader(cl)) + uidCache.put(cl,new Long(result)); } - catch (NoSuchFieldException ignore) + return result; + } + + /** + * Search for a serialVersionUID field in the given class and read + * its value. + * + * @return the contents of the serialVersionUID field + * + * @throws NoSuchFieldException if such a field does not exist or is + * not static, not final, not of type Long or not accessible. + */ + long getClassUIDFromField(Class cl) + throws NoSuchFieldException + { + long result; + + try { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + final Field suid = cl.getDeclaredField("serialVersionUID"); + SetAccessibleAction setAccessible = new SetAccessibleAction(suid); + AccessController.doPrivileged(setAccessible); + int modifiers = suid.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && suid.getType() == Long.TYPE) + result = suid.getLong(null); + else + throw new NoSuchFieldException(); } catch (IllegalAccessException ignore) { + throw new NoSuchFieldException(); } - // cl didn't define serialVersionUID, so we have to compute it - try - { - MessageDigest md; - try - { - md = MessageDigest.getInstance("SHA"); - } - catch (NoSuchAlgorithmException e) - { - // If a provider already provides SHA, use it; otherwise, use this. - Gnu gnuProvider = new Gnu(); - Security.addProvider(gnuProvider); - md = MessageDigest.getInstance("SHA"); - } - - DigestOutputStream digest_out = - new DigestOutputStream(nullOutputStream, md); - DataOutputStream data_out = new DataOutputStream(digest_out); - - data_out.writeUTF(cl.getName()); - - int modifiers = cl.getModifiers(); - // just look at interesting bits - modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL - | Modifier.INTERFACE | Modifier.PUBLIC); - data_out.writeInt(modifiers); - - // Pretend that an array has no interfaces, because when array - // serialization was defined (JDK 1.1), arrays didn't have it. - if (! cl.isArray()) - { - Class[] interfaces = cl.getInterfaces(); - Arrays.sort(interfaces, interfaceComparator); - for (int i = 0; i < interfaces.length; i++) - data_out.writeUTF(interfaces[i].getName()); - } - - Field field; - Field[] fields = cl.getDeclaredFields(); - Arrays.sort(fields, memberComparator); - for (int i = 0; i < fields.length; i++) - { - field = fields[i]; - modifiers = field.getModifiers(); - if (Modifier.isPrivate(modifiers) - && (Modifier.isStatic(modifiers) - || Modifier.isTransient(modifiers))) - continue; - - data_out.writeUTF(field.getName()); - data_out.writeInt(modifiers); - data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); - } - - // write class initializer method if present - if (VMObjectStreamClass.hasClassInitializer(cl)) - { - data_out.writeUTF("<clinit>"); - data_out.writeInt(Modifier.STATIC); - data_out.writeUTF("()V"); - } - - Constructor constructor; - Constructor[] constructors = cl.getDeclaredConstructors(); - Arrays.sort (constructors, memberComparator); - for (int i = 0; i < constructors.length; i++) - { - constructor = constructors[i]; - modifiers = constructor.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF("<init>"); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); - } - - Method method; - Method[] methods = cl.getDeclaredMethods(); - Arrays.sort(methods, memberComparator); - for (int i = 0; i < methods.length; i++) - { - method = methods[i]; - modifiers = method.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF(method.getName()); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); - } - - data_out.close(); - byte[] sha = md.digest(); - long result = 0; - int len = sha.length < 8 ? sha.length : 8; - for (int i = 0; i < len; i++) - result += (long) (sha[i] & 0xFF) << (8 * i); + return result; + } - return result; + /** + * Calculate class serial version UID for a class that does not + * define serialVersionUID: + * + * @param cl a class + * + * @return the calculated serial varsion UID. + * + * @throws NoSuchAlgorithmException if SHA algorithm not found + * + * @throws IOException if writing to the DigestOutputStream causes + * an IOException. + */ + long calculateClassUID(Class cl) + throws NoSuchAlgorithmException, IOException + { + long result; + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException - ("The SHA algorithm was not found to use in computing the Serial Version UID for class " - + cl.getName(), e); + // If a provider already provides SHA, use it; otherwise, use this. + Gnu gnuProvider = new Gnu(); + Security.addProvider(gnuProvider); + md = MessageDigest.getInstance("SHA"); + } + + DigestOutputStream digest_out = + new DigestOutputStream(nullOutputStream, md); + DataOutputStream data_out = new DataOutputStream(digest_out); + + data_out.writeUTF(cl.getName()); + + int modifiers = cl.getModifiers(); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt(modifiers); + + // Pretend that an array has no interfaces, because when array + // serialization was defined (JDK 1.1), arrays didn't have it. + if (! cl.isArray()) + { + Class[] interfaces = cl.getInterfaces(); + Arrays.sort(interfaces, interfaceComparator); + for (int i = 0; i < interfaces.length; i++) + data_out.writeUTF(interfaces[i].getName()); + } + + Field field; + Field[] fields = cl.getDeclaredFields(); + Arrays.sort(fields, memberComparator); + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers(); + if (Modifier.isPrivate(modifiers) + && (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers))) + continue; + + data_out.writeUTF(field.getName()); + data_out.writeInt(modifiers); + data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); + } + + // write class initializer method if present + if (VMObjectStreamClass.hasClassInitializer(cl)) + { + data_out.writeUTF("<clinit>"); + data_out.writeInt(Modifier.STATIC); + data_out.writeUTF("()V"); } - catch (IOException ioe) + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors(); + Arrays.sort (constructors, memberComparator); + for (int i = 0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF("<init>"); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods(); + Arrays.sort(methods, memberComparator); + for (int i = 0; i < methods.length; i++) { - throw new RuntimeException(ioe); + method = methods[i]; + modifiers = method.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF(method.getName()); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); } + + data_out.close(); + byte[] sha = md.digest(); + result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i = 0; i < len; i++) + result += (long) (sha[i] & 0xFF) << (8 * i); + + return result; } /** diff --git a/java/io/ObjectStreamField.java b/java/io/ObjectStreamField.java index b0cac5015..91f557870 100644 --- a/java/io/ObjectStreamField.java +++ b/java/io/ObjectStreamField.java @@ -65,7 +65,7 @@ public class ObjectStreamField private boolean unshared; private boolean persistent = false; private boolean toset = true; - private Field field; + Field field; ObjectStreamField (Field field) { diff --git a/java/io/PrintStream.java b/java/io/PrintStream.java index 9726ccdba..2d747c8c8 100644 --- a/java/io/PrintStream.java +++ b/java/io/PrintStream.java @@ -71,7 +71,7 @@ public class PrintStream extends FilterOutputStream implements Appendable // Line separator string. private static final char[] line_separator - = SystemProperties.getProperty("line.separator").toCharArray(); + = SystemProperties.getProperty("line.separator", "\n").toCharArray(); /** * Encoding name diff --git a/java/lang/management/ManagementFactory.java b/java/lang/management/ManagementFactory.java index d6727ea10..d9a02ee23 100644 --- a/java/lang/management/ManagementFactory.java +++ b/java/lang/management/ManagementFactory.java @@ -67,6 +67,49 @@ import javax.management.NotCompliantMBeanException; * <li>Calling the appropriate static method of this factory. * </li> * </ol> + * <h2>Open Data Types</h2> + * <p> + * The data types used by the management beans are restricted + * to <emph>open</emph> data types to aid interoperability. This + * allows the beans to be accessed remotely, including from non-Java + * clients. Below is a table which lists the types used by the beans + * on the left, and the types they are converted to when returned via + * a bean server on the right. Type information is provided for each + * bean by obtaining its instance of {@link javax.management.MBeanInfo}. + * </p> + * <table> + * <th><td>Data Type Used</td><td>Data Type Returned</td></th> + * <tr> + * <td>Primitive types (<code>int</code>, <code>char</code>, etc.)</td> + * <td>Same</td> + * </tr><tr> + * <td>Wrapper classes ({@link{java.lang.Integer}, + * @link{java.lang.Character}, etc.)</td> + * <td>Same</td> + * </tr><tr> + * <td>An {@link java.lang.Enum}</td> + * <td>The <code>name</code> of the enumeration constant</td> + * </tr><tr> + * <td>An array of type <code>E</code></td> + * <td>An array of the same dimensions with this mapping applied + * to <code>E</code>.</td> + * </tr><tr> + * <td>A class with `getter' methods and a + * <code>from({@link javax.management.openmbean.CompositeData})</code> + * method.</td> + * <td>The equivalent {@link javax.management.openmbean.CompositeData} + * instance, specified by the <code>from</code> method.</td> + * </tr><tr> + * <td>A map with keys of type <code>K</code> and values of + * type <code>V</code>.</td> + * <td>A {@link javax.management.openmbean.TabularData} instance, + * with the row type containing two items, <code>"key"</code> and + * <code>"value"</code> with the types <code>K</code> and <code>V</code> + * respectively (with translation applied).</td> + * </tr><tr> + * <td>A list of type <code>E</code>.</td> + * <td>An array with this mapping applied to <code>E</code>.</td> + * </tr></table> * * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @since 1.5 diff --git a/java/lang/ref/Reference.java b/java/lang/ref/Reference.java index 8a7501863..ce224b891 100644 --- a/java/lang/ref/Reference.java +++ b/java/lang/ref/Reference.java @@ -1,5 +1,5 @@ /* java.lang.ref.Reference - Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -82,7 +82,7 @@ public abstract class Reference<T> * The queue this reference is registered on. This is null, if this * wasn't registered to any queue or reference was already enqueued. */ - ReferenceQueue<? super T> queue; + volatile ReferenceQueue<? super T> queue; /** * Link to the next entry on the queue. If this is null, this @@ -91,7 +91,7 @@ public abstract class Reference<T> * (not to null, that value is used to mark a not enqueued * reference). */ - Reference nextOnQueue; + volatile Reference nextOnQueue; /** * This lock should be taken by the garbage collector, before @@ -166,11 +166,10 @@ public abstract class Reference<T> */ public boolean enqueue() { - if (queue != null && nextOnQueue == null) + ReferenceQueue q = queue; + if (q != null) { - queue.enqueue(this); - queue = null; - return true; + return q.enqueue(this); } return false; } diff --git a/java/lang/ref/ReferenceQueue.java b/java/lang/ref/ReferenceQueue.java index 189b81b17..281628779 100644 --- a/java/lang/ref/ReferenceQueue.java +++ b/java/lang/ref/ReferenceQueue.java @@ -1,5 +1,5 @@ /* java.lang.ref.ReferenceQueue - Copyright (C) 1999, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,6 +63,12 @@ public class ReferenceQueue<T> private Reference<? extends T> first; /** + * This is the lock that protects our linked list and is used to signal + * a thread waiting in remove(). + */ + private final Object lock = new Object(); + + /** * Creates a new empty reference queue. */ public ReferenceQueue() @@ -76,7 +82,7 @@ public class ReferenceQueue<T> * @return a reference on the queue, if there is one, * <code>null</code> otherwise. */ - public synchronized Reference<? extends T> poll() + public Reference<? extends T> poll() { return dequeue(); } @@ -84,14 +90,23 @@ public class ReferenceQueue<T> /** * This is called by reference to enqueue itself on this queue. * @param ref the reference that should be enqueued. + * @return true if successful, false if not. */ - synchronized void enqueue(Reference<? extends T> ref) - { - /* last reference will point to itself */ - ref.nextOnQueue = first == null ? ref : first; - first = ref; - /* this wakes only one remove thread. */ - notify(); + final boolean enqueue(Reference<? extends T> ref) + { + synchronized (lock) + { + if (ref.queue != this) + return false; + + /* last reference will point to itself */ + ref.nextOnQueue = first == null ? ref : first; + ref.queue = null; + first = ref; + /* this wakes only one remove thread. */ + lock.notify(); + return true; + } } /** @@ -100,13 +115,16 @@ public class ReferenceQueue<T> */ private Reference<? extends T> dequeue() { - if (first == null) - return null; - - Reference<? extends T> result = first; - first = (first == first.nextOnQueue) ? null : first.nextOnQueue; - result.nextOnQueue = null; - return result; + synchronized (lock) + { + if (first == null) + return null; + + Reference<? extends T> result = first; + first = (first == first.nextOnQueue) ? null : first.nextOnQueue; + result.nextOnQueue = null; + return result; + } } /** @@ -118,12 +136,13 @@ public class ReferenceQueue<T> * <code>null</code> if timeout period expired. * @exception InterruptedException if the wait was interrupted. */ - public synchronized Reference<? extends T> remove(long timeout) + public Reference<? extends T> remove(long timeout) throws InterruptedException { - if (first == null) + synchronized (lock) { - wait(timeout); + if (first == null) + lock.wait(timeout); } return dequeue(); diff --git a/java/math/BigInteger.java b/java/math/BigInteger.java index 0a683f7b7..c897d8bf4 100644 --- a/java/math/BigInteger.java +++ b/java/math/BigInteger.java @@ -1,5 +1,5 @@ /* java.math.BigInteger -- Arbitary precision integers - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -198,8 +198,20 @@ public class BigInteger extends Number implements Comparable<BigInteger> private void init(int numBits, Random rnd) { int highbits = numBits & 31; + // minimum number of bytes to store the above number of bits + int highBitByteCount = (highbits + 7) / 8; + // number of bits to discard from the last byte + int discardedBitCount = highbits % 8; + if (discardedBitCount != 0) + discardedBitCount = 8 - discardedBitCount; + byte[] highBitBytes = new byte[highBitByteCount]; if (highbits > 0) - highbits = rnd.nextInt() >>> (32 - highbits); + { + rnd.nextBytes(highBitBytes); + highbits = (highBitBytes[highBitByteCount - 1] & 0xFF) >>> discardedBitCount; + for (int i = highBitByteCount - 2; i >= 0; i--) + highbits = (highbits << 8) | (highBitBytes[i] & 0xFF); + } int nwords = numBits / 32; while (highbits == 0 && nwords > 0) @@ -226,8 +238,13 @@ public class BigInteger extends Number implements Comparable<BigInteger> this(bitLength, rnd); // Keep going until we find a probable prime. + BigInteger result; while (true) { + // ...but first ensure that BI has bitLength bits + result = setBit(bitLength - 1); + this.ival = result.ival; + this.words = result.words; if (isProbablePrime(certainty)) return; @@ -1583,24 +1600,31 @@ public class BigInteger extends Number implements Comparable<BigInteger> // but slightly more expensive, for little practical gain. if (len <= 15 && radix <= 16) return valueOf(Long.parseLong(s, radix)); - + + int i, digit; + boolean negative; + byte[] bytes; + char ch = s.charAt(0); + if (ch == '-') + { + negative = true; + i = 1; + bytes = new byte[len - 1]; + } + else + { + negative = false; + i = 0; + bytes = new byte[len]; + } int byte_len = 0; - byte[] bytes = new byte[len]; - boolean negative = false; - for (int i = 0; i < len; i++) + for ( ; i < len; i++) { - char ch = s.charAt(i); - if (ch == '-') - negative = true; - else if (ch == '_' || (byte_len == 0 && (ch == ' ' || ch == '\t'))) - continue; - else - { - int digit = Character.digit(ch, radix); - if (digit < 0) - break; - bytes[byte_len++] = (byte) digit; - } + ch = s.charAt(i); + digit = Character.digit(ch, radix); + if (digit < 0) + throw new NumberFormatException(); + bytes[byte_len++] = (byte) digit; } return valueOf(bytes, byte_len, negative, radix); } diff --git a/java/net/SocketPermission.java b/java/net/SocketPermission.java index 723ccc7a5..97e93dcbb 100644 --- a/java/net/SocketPermission.java +++ b/java/net/SocketPermission.java @@ -164,24 +164,69 @@ public final class SocketPermission extends Permission implements Serializable */ public SocketPermission(String hostport, String actions) { - super(hostport); + super(processHostport(hostport)); - setHostPort(hostport); + setHostPort(getName()); setActions(actions); } /** + * There are two cases in which hostport needs rewriting before + * being passed to the superclass constructor. If hostport is an + * empty string then it is substituted with "localhost". And if + * the host part of hostport is a literal IPv6 address in the full + * uncompressed form not enclosed with "[" and "]" then we enclose + * it with them. + */ + private static String processHostport(String hostport) + { + if (hostport.length() == 0) + return "localhost"; + + if (hostport.charAt(0) == '[') + return hostport; + + int colons = 0, last_colon = 0; + for (int i = 0; i < hostport.length(); i++) + { + if (hostport.charAt(i) == ':') + { + if (i - last_colon == 1) + throw new IllegalArgumentException("Ambiguous hostport part"); + colons++; + last_colon = i; + } + } + + switch (colons) + { + case 0: + case 1: + // a hostname or IPv4 address + return hostport; + + case 7: + // an IPv6 address with no ports + return "[" + hostport + "]"; + + case 8: + // an IPv6 address with ports + return "[" + hostport.substring(0, last_colon) + "]" + + hostport.substring(last_colon); + + default: + throw new IllegalArgumentException("Ambiguous hostport part"); + } + } + + /** * Parse the hostport argument to the constructor. */ private void setHostPort(String hostport) { // Split into host and ports String ports; - if (hostport.length() == 0) - { - host = ports = ""; - } - else if (hostport.charAt(0) == '[') + if (hostport.charAt(0) == '[') { // host is a bracketed IPv6 address int end = hostport.indexOf("]"); @@ -211,8 +256,6 @@ public final class SocketPermission extends Permission implements Serializable ports = hostport.substring(sep + 1); } } - if (ports.indexOf(":") != -1) - throw new IllegalArgumentException("Unexpected ':'"); // Parse and validate the ports if (ports.length() == 0) diff --git a/java/security/AlgorithmParameterGenerator.java b/java/security/AlgorithmParameterGenerator.java index e33fbaf81..e2a17d4bf 100644 --- a/java/security/AlgorithmParameterGenerator.java +++ b/java/security/AlgorithmParameterGenerator.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; /** @@ -97,26 +98,29 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. + * @param algorithm the name of algorithm to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if <code>algorithm</code> is not implemented by any provider. + * @throws NoSuchAlgorithmException if <code>algorithm</code> is not + * implemented by any provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static AlgorithmParameterGenerator getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -124,27 +128,27 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the name of the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the name of the {@link Provider} to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static AlgorithmParameterGenerator getInstance(String algorithm, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -152,38 +156,50 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the {@link Provider} to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static AlgorithmParameterGenerator getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder() + .append("AlgorithmParameterGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new AlgorithmParameterGenerator( - (AlgorithmParameterGeneratorSpi) Engine.getInstance( - ALGORITHM_PARAMETER_GENERATOR, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(ALGORITHM_PARAMETER_GENERATOR, + algorithm, + provider); + return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, + provider, + algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** @return the {@link Provider} of this generator. */ diff --git a/java/security/AlgorithmParameters.java b/java/security/AlgorithmParameters.java index 911c566c8..f5e5063a1 100644 --- a/java/security/AlgorithmParameters.java +++ b/java/security/AlgorithmParameters.java @@ -41,6 +41,7 @@ package java.security; import gnu.java.security.Engine; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; @@ -91,106 +92,115 @@ public class AlgorithmParameters /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters. + * <p> + * The returned <code>AlgorithmParameters</code> must still be initialized + * with an <code>init()</code> method. * - * <p>The returned <code>AlgorithmParameters</code> must still be initialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. + * @param algorithm the algorithm to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static AlgorithmParameters getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore this. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters from a named provider. + * <p> + * The returned <code>AlgorithmParameters</code> must still be intialized + * with an <code>init()</code> method. + * </p> * - * <p>The returned <code>AlgorithmParameters</code> must still be intialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. - * @param provider - * the name of the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the name of the {@link Provider} to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code> or is an empty - * string. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ - public static AlgorithmParameters getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static AlgorithmParameters getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters from the specified {@link Provider}. + * <p> + * The returned <code>AlgorithmParameters</code> must still be intialized + * with an <code>init()</code> method. * - * <p>The returned <code>AlgorithmParameters</code> must still be intialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the {@link Provider} to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the {@link Provider}. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 */ public static AlgorithmParameters getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder("AlgorithmParameters for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new AlgorithmParameters((AlgorithmParametersSpi) - Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider); + return new AlgorithmParameters((AlgorithmParametersSpi) spi, + provider, + algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** @return the provider of this parameter object. */ diff --git a/java/security/KeyFactory.java b/java/security/KeyFactory.java index 1ecfd2f72..043dd59a1 100644 --- a/java/security/KeyFactory.java +++ b/java/security/KeyFactory.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; @@ -93,26 +94,29 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory. * - * @param algorithm - * the name of algorithm to use. + * @param algorithm the name of algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static KeyFactory getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -120,29 +124,26 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory from the specified provider. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the name of algorithm to use. + * @param provider the name of the provider to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code> or is an empty - * string. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyFactory getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -150,38 +151,44 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory from the designated {@link Provider}. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static KeyFactory getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder("KeyFactory for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new KeyFactory((KeyFactorySpi) - Engine.getInstance(KEY_FACTORY, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(KEY_FACTORY, algorithm, provider); + return new KeyFactory((KeyFactorySpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); - } + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** diff --git a/java/security/KeyPairGenerator.java b/java/security/KeyPairGenerator.java index 357d7a75f..6974035fd 100644 --- a/java/security/KeyPairGenerator.java +++ b/java/security/KeyPairGenerator.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; /** @@ -90,28 +91,29 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * Returns a new instance of <code>KeyPairGenerator</code> which generates * key-pairs for the specified algorithm. * - * @param algorithm - * the name of the algorithm to use. + * @param algorithm the name of the algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static KeyPairGenerator getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -119,23 +121,26 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * Returns a new instance of <code>KeyPairGenerator</code> which generates * key-pairs for the specified algorithm from a named provider. * - * @param algorithm - * the name of the algorithm to use. - * @param provider - * the name of a {@link Provider} to use. + * @param algorithm the name of the algorithm to use. + * @param provider the name of a {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyPairGenerator getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -148,10 +153,11 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * @param provider * the {@link Provider} to use. * @return a new insatnce repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. * @throws NoSuchAlgorithmException * if the algorithm is not implemented by the {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ @@ -159,20 +165,27 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - Object o = null; + StringBuilder sb = new StringBuilder("KeyPairGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(KEY_PAIR_GENERATOR, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - - KeyPairGenerator result = null; + KeyPairGenerator result; if (o instanceof KeyPairGenerator) { result = (KeyPairGenerator) o; @@ -180,7 +193,11 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi } else if (o instanceof KeyPairGeneratorSpi) result = new DummyKeyPairGenerator((KeyPairGeneratorSpi) o, algorithm); - + else + { + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); + } result.provider = provider; return result; } diff --git a/java/security/KeyStore.java b/java/security/KeyStore.java index e31e8a6c9..1d036c31c 100644 --- a/java/security/KeyStore.java +++ b/java/security/KeyStore.java @@ -43,6 +43,7 @@ import gnu.java.security.Engine; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.security.cert.CertificateException; import java.util.Date; import java.util.Enumeration; @@ -108,105 +109,100 @@ public class KeyStore this.type = type; } - // Class methods. - // ------------------------------------------------------------------------ - - /** - * Gets an instance of the KeyStore class representing - * the specified keystore. If the type is not - * found then, it throws KeyStoreException. - * - * @param type the type of keystore to choose - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not implemented - * by providers or the implementation cannot be instantiated. + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the first provider that implements it. + * + * @param type the type of keystore to create. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type of is not implemented by + * any provider, or the implementation could not be instantiated. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static KeyStore getInstance(String type) throws KeyStoreException { Provider[] p = Security.getProviders(); - + KeyStoreException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, p[i]); - } - catch (KeyStoreException e) - { - // Ignore. - } - } - + try + { + return getInstance(type, p[i]); + } + catch (KeyStoreException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new KeyStoreException(type); } - /** - * Gets an instance of the KeyStore class representing - * the specified key store from the specified provider. - * If the type is not found then, it throws KeyStoreException. - * If the provider is not found, then it throws - * NoSuchProviderException. - * - * @param type the type of keystore to choose - * @param provider the provider name - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not - * implemented by the given provider - * @throws NoSuchProviderException if the provider is not found - * @throws IllegalArgumentException if the provider string is - * null or empty + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the named provider. + * + * @param type the type of keystore to create. + * @param provider the name of the provider to use. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type is not implemented by the + * given provider. + * @throws NoSuchProviderException if the provider is not found. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyStore getInstance(String type, String provider) throws KeyStoreException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(type, p); } - /** - * Gets an instance of the KeyStore class representing - * the specified key store from the specified provider. - * If the type is not found then, it throws KeyStoreException. - * If the provider is not found, then it throws - * NoSuchProviderException. - * - * @param type the type of keystore to choose - * @param provider the keystore provider - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not - * implemented by the given provider - * @throws IllegalArgumentException if the provider object is null + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the specified provider. + * + * @param type the type of keystore to create. + * @param provider the provider to use. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type is not implemented by the + * given provider. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. * @since 1.4 */ public static KeyStore getInstance(String type, Provider provider) - throws KeyStoreException + throws KeyStoreException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); + Throwable cause; try { - return new KeyStore( - (KeyStoreSpi) Engine.getInstance(KEY_STORE, type, provider), - provider, type); + Object spi = Engine.getInstance(KEY_STORE, type, provider); + return new KeyStore((KeyStoreSpi) spi, provider, type); } - catch (NoSuchAlgorithmException nsae) + catch (NoSuchAlgorithmException x) { - throw new KeyStoreException(type); + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new KeyStoreException(type); + cause = x.getCause() != null ? x.getCause() : x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new KeyStoreException(type); + cause = x; } + KeyStoreException x = new KeyStoreException(type); + x.initCause(cause); + throw x; } /** diff --git a/java/security/MessageDigest.java b/java/security/MessageDigest.java index 8a5de92df..0f8e934e5 100644 --- a/java/security/MessageDigest.java +++ b/java/security/MessageDigest.java @@ -40,6 +40,8 @@ package java.security; import gnu.java.security.Engine; import java.nio.ByteBuffer; +import java.lang.reflect.InvocationTargetException; + /** * Message digests are secure one-way hash functions that take arbitrary-sized * data and output a fixed-length hash value. @@ -73,28 +75,29 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm. * - * @param algorithm - * the name of the digest algorithm to use. + * @param algorithm the name of the digest algorithm to use. * @return a new instance representing the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static MessageDigest getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException ignored) - { - // Ignore. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -102,29 +105,26 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm from a named provider. * - * @param algorithm - * the name of the digest algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the name of the digest algorithm to use. + * @param provider the name of the provider to use. * @return a new instance representing the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static MessageDigest getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider != null) - provider = provider.trim(); - - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -132,39 +132,43 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm from a designated {@link Provider}. * - * @param algorithm - * the name of the digest algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of the digest algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance representing the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static MessageDigest getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - MessageDigest result = null; - Object o = null; + StringBuilder sb = new StringBuilder("MessageDigest for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(MESSAGE_DIGEST, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - + MessageDigest result; if (o instanceof MessageDigestSpi) - { - result = new DummyMessageDigest((MessageDigestSpi) o, algorithm); - } + result = new DummyMessageDigest((MessageDigestSpi) o, algorithm); else if (o instanceof MessageDigest) { result = (MessageDigest) o; @@ -172,7 +176,8 @@ public abstract class MessageDigest extends MessageDigestSpi } else { - throw new NoSuchAlgorithmException(algorithm); + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); } result.provider = provider; return result; diff --git a/java/security/SecureClassLoader.java b/java/security/SecureClassLoader.java index bcc3cab88..dfc1758b5 100644 --- a/java/security/SecureClassLoader.java +++ b/java/security/SecureClassLoader.java @@ -1,5 +1,5 @@ /* SecureClassLoader.java --- A Secure Class Loader - Copyright (C) 1999, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ package java.security; import java.util.WeakHashMap; +import java.nio.ByteBuffer; +import java.util.HashMap; + /** * A Secure Class Loader for loading classes with additional * support for specifying code source and permissions when @@ -50,22 +53,16 @@ import java.util.WeakHashMap; */ public class SecureClassLoader extends ClassLoader { - WeakHashMap<CodeSource, ProtectionDomain> protectionDomainCache - = new WeakHashMap<CodeSource, ProtectionDomain>(); + private final HashMap<CodeSource,ProtectionDomain> protectionDomainCache + = new HashMap<CodeSource, ProtectionDomain>(); protected SecureClassLoader(ClassLoader parent) { super(parent); - SecurityManager sm = System.getSecurityManager(); - if(sm != null) - sm.checkCreateClassLoader(); } protected SecureClassLoader() { - SecurityManager sm = System.getSecurityManager(); - if(sm != null) - sm.checkCreateClassLoader(); } /** @@ -85,10 +82,35 @@ public class SecureClassLoader extends ClassLoader protected final Class<?> defineClass(String name, byte[] b, int off, int len, CodeSource cs) { + return super.defineClass(name, b, off, len, getProtectionDomain(cs)); + } + + /** + * Creates a class using an ByteBuffer and a + * CodeSource. + * + * @param name the name to give the class. null if unknown. + * @param b the data representing the classfile, in classfile format. + * @param cs the CodeSource for the class or null when unknown. + * + * @return the class that was defined and optional CodeSource. + * + * @exception ClassFormatError if the byte array is not in proper classfile format. + * + * @since 1.5 + */ + protected final Class defineClass(String name, ByteBuffer b, CodeSource cs) + { + return super.defineClass(name, b, getProtectionDomain(cs)); + } + + /* Lookup or create a protection domain for the CodeSource, + * if CodeSource is null it will return null. */ + private ProtectionDomain getProtectionDomain(CodeSource cs) + { + ProtectionDomain protectionDomain = null; if (cs != null) { - ProtectionDomain protectionDomain; - synchronized (protectionDomainCache) { protectionDomain = (ProtectionDomain)protectionDomainCache.get(cs); @@ -108,10 +130,8 @@ public class SecureClassLoader extends ClassLoader protectionDomain = domain; } } - return super.defineClass(name, b, off, len, protectionDomain); - } - else - return super.defineClass(name, b, off, len); + } + return protectionDomain; } /** @@ -120,7 +140,7 @@ public class SecureClassLoader extends ClassLoader * java.security.Policy.getPermissions. * * This method is called by defineClass that takes a CodeSource - * arguement to build a proper ProtectionDomain for the class + * argument to build a proper ProtectionDomain for the class * being defined. */ protected PermissionCollection getPermissions(CodeSource cs) diff --git a/java/security/SecureRandom.java b/java/security/SecureRandom.java index c66963e8f..005f4670e 100644 --- a/java/security/SecureRandom.java +++ b/java/security/SecureRandom.java @@ -45,6 +45,7 @@ import gnu.java.security.jce.prng.Sha160RandomSpi; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; @@ -187,101 +188,106 @@ public class SecureRandom extends Random this.algorithm = algorithm; } - // Class methods. - // ------------------------------------------------------------------------ - /** - * Returns an instance of a SecureRandom. It creates the class from - * the first provider that implements it. - * + * Returns an instance of a <code>SecureRandom</code> from the first provider + * that implements it. + * * @param algorithm The algorithm name. - * @return A new SecureRandom implementing the given algorithm. - * @throws NoSuchAlgorithmException If no installed provider implements - * the given algorithm. + * @return A new <code>SecureRandom</code> implementing the given algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * given algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static SecureRandom getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - } - - // None found. + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Returns an instance of a SecureRandom. It creates the class - * for the specified algorithm from the named provider. - * + * Returns an instance of a <code>SecureRandom</code> for the specified + * algorithm from the named provider. + * * @param algorithm The algorithm name. - * @param provider The provider name. - * @return A new SecureRandom implementing the chosen algorithm. + * @param provider The provider name. + * @return A new <code>SecureRandom</code> implementing the chosen + * algorithm. * @throws NoSuchAlgorithmException If the named provider does not implement - * the algorithm, or if the implementation cannot be - * instantiated. - * @throws NoSuchProviderException If no provider named - * <code>provider</code> is currently installed. - * @throws IllegalArgumentException If <code>provider</code> is null - * or is empty. + * the algorithm, or if the implementation cannot be instantiated. + * @throws NoSuchProviderException If no provider named <code>provider</code> + * is currently installed. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static SecureRandom getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** - * Returns an instance of a SecureRandom. It creates the class for - * the specified algorithm from the given provider. - * - * @param algorithm The SecureRandom algorithm to create. - * @param provider The provider to get the instance from. - * @throws NoSuchAlgorithmException If the algorithm cannot be found, or - * if the class cannot be instantiated. - * @throws IllegalArgumentException If <code>provider</code> is null. + * Returns an instance of a <code>SecureRandom</code> for the specified + * algorithm from the given provider. + * + * @param algorithm The <code>SecureRandom</code> algorithm to create. + * @param provider The provider to use. + * @throws NoSuchAlgorithmException If the algorithm cannot be found, or if + * the class cannot be instantiated. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static SecureRandom getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); + StringBuilder sb = new StringBuilder("SecureRandom for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new SecureRandom((SecureRandomSpi) - Engine.getInstance(SECURE_RANDOM, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SECURE_RANDOM, algorithm, provider); + return new SecureRandom((SecureRandomSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** Returns the provider being used by the current SecureRandom class. diff --git a/java/security/Signature.java b/java/security/Signature.java index 68ae99d42..1245707f7 100644 --- a/java/security/Signature.java +++ b/java/security/Signature.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -128,28 +129,29 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature. * - * @param algorithm - * the algorithm to use. + * @param algorithm the algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static Signature getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -157,28 +159,26 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature from the named provider. * - * @param algorithm - * the algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the algorithm to use. + * @param provider the name of the provider to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException if <code>provider</code> is - * <code>null</code> or is an empty string. - * @throws NoSuchProviderException - * if the named provider was not found. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static Signature getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -186,35 +186,41 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature from the specified {@link Provider}. * - * @param algorithm - * the algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static Signature getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - Signature result = null; - Object o = null; + StringBuilder sb = new StringBuilder("Signature algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(SIGNATURE, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - + Signature result; if (o instanceof SignatureSpi) - { - result = new DummySignature((SignatureSpi) o, algorithm); - } + result = new DummySignature((SignatureSpi) o, algorithm); else if (o instanceof Signature) { result = (Signature) o; @@ -222,7 +228,8 @@ public abstract class Signature extends SignatureSpi } else { - throw new NoSuchAlgorithmException(algorithm); + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); } result.provider = provider; return result; diff --git a/java/security/cert/CertPathBuilder.java b/java/security/cert/CertPathBuilder.java index f6965205f..519ed2b6c 100644 --- a/java/security/cert/CertPathBuilder.java +++ b/java/security/cert/CertPathBuilder.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -111,50 +112,54 @@ public class CertPathBuilder } /** - * Get an instance of a named CertPathBuilder, from the first provider - * that implements it. - * - * @param algorithm The name of the CertPathBuilder to create. + * Returns an instance of a named <code>CertPathBuilder</code> from the + * first provider that implements it. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static CertPathBuilder getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Get an instance of a named CertPathBuilder from the named + * Returns an instance of a named <code>CertPathBuilder</code> from a named * provider. - * - * @param algorithm The name of the CertPathBuilder to create. - * @param provider The name of the provider from which to get the - * implementation. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. + * @param provider The name of the provider to use. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. - * @throws NoSuchProviderException If the named provider does not - * exist. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathBuilder getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); @@ -162,41 +167,47 @@ public class CertPathBuilder } /** - * Get an instance of a named CertPathBuilder from the specified - * provider. - * - * @param algorithm The name of the CertPathBuilder to create. - * @param provider The provider from which to get the implementation. + * Returns an instance of a named <code>CertPathBuilder</code> from the + * specified provider. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. + * @param provider The provider to use. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. - * @throws IllegalArgumentException If <i>provider</i> in - * <tt>null</tt>. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathBuilder getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); + StringBuilder sb = new StringBuilder("CertPathBuilder for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertPathBuilder((CertPathBuilderSpi) - Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider); + return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Return the name of this CertPathBuilder algorithm. * diff --git a/java/security/cert/CertPathValidator.java b/java/security/cert/CertPathValidator.java index 5fed19e9a..bf7c9746e 100644 --- a/java/security/cert/CertPathValidator.java +++ b/java/security/cert/CertPathValidator.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -124,91 +125,103 @@ public class CertPathValidator { } /** - * Get an instance of the given validator from the first provider that + * Returns an instance of the given validator from the first provider that * implements it. - * + * * @param algorithm The name of the algorithm to get. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the requested algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * requested algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static CertPathValidator getInstance(String algorithm) throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Get an instance of the given validator from the named provider. - * + * Returns an instance of the given validator from the named provider. + * * @param algorithm The name of the algorithm to get. - * @param provider The name of the provider from which to get the - * implementation. + * @param provider The name of the provider from which to get the + * implementation. * @return The new instance. - * @throws NoSuchAlgorithmException If the named provider does not - * implement the algorithm. - * @throws NoSuchProviderException If no provider named - * <i>provider</i> is installed. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm. + * @throws NoSuchProviderException If no provider named <i>provider</i> is + * installed. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static CertPathValidator getInstance(String algorithm, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static CertPathValidator getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** - * Get an instance of the given validator from the given provider. - * + * Returns an instance of the given validator from the given provider. + * * @param algorithm The name of the algorithm to get. - * @param provider The provider from which to get the implementation. + * @param provider The provider from which to get the implementation. * @return The new instance. - * @throws NoSuchAlgorithmException If the provider does not implement - * the algorithm. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws NoSuchAlgorithmException If the provider does not implement the + * algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathValidator getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + StringBuilder sb = new StringBuilder("CertPathValidator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertPathValidator((CertPathValidatorSpi) - Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider); + return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Return the name of this validator. * diff --git a/java/security/cert/CertStore.java b/java/security/cert/CertStore.java index 2d5d944db..a27086562 100644 --- a/java/security/cert/CertStore.java +++ b/java/security/cert/CertStore.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -123,59 +124,63 @@ public class CertStore } /** - * Get an instance of the given certificate store from the first + * Returns an instance of the given certificate store type from the first * installed provider. - * - * @param type The type of CertStore to create. - * @param params The parameters to initialize this cert store with. + * + * @param type The type of <code>CertStore</code> to create. + * @param params The parameters to initialize this cert store with. * @return The new instance. - * @throws InvalidAlgorithmParameterException If the instance rejects - * the specified parameters. - * @throws NoSuchAlgorithmException If no installed provider - * implements the specified CertStore. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws InvalidAlgorithmParameterException If the instance rejects the + * specified parameters. + * @throws NoSuchAlgorithmException If no installed provider implements the + * specified CertStore. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, params, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(type, params, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(type); } /** - * Get an instance of the given certificate store from the named + * Returns an instance of the given certificate store type from a named * provider. - * - * @param type The type of CertStore to create. - * @param params The parameters to initialize this cert store with. - * @param provider The name of the provider from which to get the - * implementation. + * + * @param type The type of <code>CertStore</code> to create. + * @param params The parameters to initialize this cert store with. + * @param provider The name of the provider to use. * @return The new instance. - * @throws InvalidAlgorithmParameterException If the instance rejects - * the specified parameters. + * @throws InvalidAlgorithmParameterException If the instance rejects the + * specified parameters. * @throws NoSuchAlgorithmException If the specified provider does not - * implement the specified CertStore. - * @throws NoSuchProviderException If no provider named - * <i>provider</i> is installed. - * @throws IllegalArgumentException If <i>provider</i> is null. + * implement the specified CertStore. + * @throws NoSuchProviderException If no provider named <i>provider</i> is + * installed. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params, String provider) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); @@ -183,48 +188,52 @@ public class CertStore } /** - * Get an instance of the given certificate store from the given + * Returns an instance of the given certificate store type from a given * provider. * - * @param type The type of CertStore to create. + * @param type The type of <code>CertStore</code> to create. * @param params The parameters to initialize this cert store with. - * @param provider The provider from which to get the implementation. + * @param provider The provider to use. * @return The new instance. * @throws InvalidAlgorithmParameterException If the instance rejects * the specified parameters. * @throws NoSuchAlgorithmException If the specified provider does not * implement the specified CertStore. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params, Provider provider) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + StringBuilder sb = new StringBuilder("CertStore of type [") + .append(type).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertStore((CertStoreSpi) Engine.getInstance(CERT_STORE, - type, provider, new Object[] { params }), provider, type, params); + Object[] args = new Object[] { params }; + Object spi = Engine.getInstance(CERT_STORE, type, provider, args); + return new CertStore((CertStoreSpi) spi, provider, type, params); } - catch (ClassCastException cce) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(type); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (ClassCastException x) { - Throwable cause = ite.getCause(); - if (cause instanceof InvalidAlgorithmParameterException) - throw (InvalidAlgorithmParameterException) cause; - else - throw new NoSuchAlgorithmException(type); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } -// Instance methods. - // ------------------------------------------------------------------------ - /** * Return the type of certificate store this instance represents. * diff --git a/java/security/cert/CertificateFactory.java b/java/security/cert/CertificateFactory.java index 363c750cc..8139c6ec5 100644 --- a/java/security/cert/CertificateFactory.java +++ b/java/security/cert/CertificateFactory.java @@ -41,6 +41,8 @@ package java.security.cert; import gnu.java.security.Engine; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; @@ -84,106 +86,102 @@ public class CertificateFactory this.type = type; } -// Class methods. - // ------------------------------------------------------------------------ - - /** - * Gets an instance of the CertificateFactory class representing - * the specified certificate factory. If the type is not - * found then, it throws CertificateException. - * - * @param type The type of certificate factory to create. - * @return a CertificateFactory repesenting the desired type - * @throws CertificateException If the type of certificate is not - * implemented by any installed provider. + /** + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type. + * + * @param type The type of certificate factory to create. + * @return A <code>CertificateFactory</code> of the desired type. + * @throws CertificateException If the type of certificate factory is not + * implemented by any installed provider. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static final CertificateFactory getInstance(String type) - throws CertificateException + throws CertificateException { Provider[] p = Security.getProviders(); - + CertificateException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, p[i]); - } - catch (CertificateException e) - { - // Ignored. - } - } - + try + { + return getInstance(type, p[i]); + } + catch (CertificateException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new CertificateException(type); } - /** - * Gets an instance of the CertificateFactory class representing - * the specified certificate factory from the specified provider. - * If the type is not found then, it throws {@link CertificateException}. - * If the provider is not found, then it throws - * {@link java.security.NoSuchProviderException}. - * - * @param type The type of certificate factory to create. - * @param provider The name of the provider from which to get the - * implementation. - * @return A CertificateFactory for the desired type. - * @throws CertificateException If the type of certificate is not - * implemented by the named provider. + /** + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type from the named provider. + * + * @param type The type of certificate factory to create. + * @param provider The name of the provider to use. + * @return A <code>CertificateFactory</code> for the desired type. + * @throws CertificateException If the type of certificate is not implemented + * by the named provider. * @throws NoSuchProviderException If the named provider is not installed. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static final CertificateFactory getInstance(String type, String provider) throws CertificateException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); - if( p == null) + if (p == null) throw new NoSuchProviderException(provider); - return getInstance(type, p); } /** - * Get a certificate factory for the given certificate type from the - * given provider. - * - * @param type The type of certificate factory to create. + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type from the designated provider. + * + * @param type The type of certificate factory to create. * @param provider The provider from which to get the implementation. - * @return A CertificateFactory for the desired type. - * @throws CertificateException If the type of certificate is not - * implemented by the provider. - * @throws IllegalArgumentException If the provider is null. + * @return A <code>CertificateFactory</code> for the desired type. + * @throws CertificateException If the type of certificate is not implemented + * by the provider. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static final CertificateFactory getInstance(String type, Provider provider) - throws CertificateException + throws CertificateException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + Throwable cause; try { - return new CertificateFactory((CertificateFactorySpi) - Engine.getInstance(CERTIFICATE_FACTORY, type, provider), - provider, type); + Object spi = Engine.getInstance(CERTIFICATE_FACTORY, type, provider); + return new CertificateFactory((CertificateFactorySpi) spi, provider, type); } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new CertificateException(type); + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new CertificateException(type); + cause = x.getCause() != null ? x.getCause() : x; } - catch (NoSuchAlgorithmException nsae) + catch (NoSuchAlgorithmException x) { - throw new CertificateException(nsae.getMessage()); + cause = x; } + CertificateException x = new CertificateException(type); + x.initCause(cause); + throw x; } -// Instance methods. - // ------------------------------------------------------------------------ - /** * Gets the provider of this implementation. * diff --git a/java/util/Calendar.java b/java/util/Calendar.java index 6865230c8..8c46c0193 100644 --- a/java/util/Calendar.java +++ b/java/util/Calendar.java @@ -1,5 +1,6 @@ /* Calendar.java -- - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -484,6 +485,8 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the default * time zone and locale. + * + * @return The new calendar. */ public static synchronized Calendar getInstance() { @@ -493,7 +496,12 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the given * time zone and the default locale. - * @param zone a time zone. + * + * @param zone a time zone (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>zone</code> is <code>null</code>. */ public static synchronized Calendar getInstance(TimeZone zone) { @@ -503,7 +511,12 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the default * time zone and the given locale. - * @param locale a locale. + * + * @param locale a locale (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>locale</code> is <code>null</code>. */ public static synchronized Calendar getInstance(Locale locale) { @@ -525,8 +538,14 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the given * time zone and locale. - * @param zone a time zone. - * @param locale a locale. + * + * @param zone a time zone (<code>null</code> not permitted). + * @param locale a locale (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>zone</code> or <code>locale</code> + * is <code>null</code>. */ public static synchronized Calendar getInstance(TimeZone zone, Locale locale) { @@ -618,6 +637,10 @@ public abstract class Calendar /** * Sets this Calendar's time to the given Date. All time fields * are invalidated by this method. + * + * @param date the date (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>date</code> is <code>null</code>. */ public final void setTime(Date date) { diff --git a/java/util/Locale.java b/java/util/Locale.java index d2aead43c..001c7afde 100644 --- a/java/util/Locale.java +++ b/java/util/Locale.java @@ -188,11 +188,17 @@ public final class Locale implements Serializable, Cloneable private String variant; /** - * This is the cached hashcode. When writing to stream, we write -1. + * This is where the JDK caches its hashcode. This is is only here + * for serialization purposes. The actual cache is hashcodeCache * * @serial should be -1 in serial streams */ - private transient int hashcode; + private int hashcode = -1; + + /** + * This is the cached hashcode. + */ + private transient int hashcodeCache; /** * Array storing all available locales. @@ -324,7 +330,7 @@ public final class Locale implements Serializable, Cloneable this.language = language; this.country = country; this.variant = variant; - hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + hashcodeCache = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); } /** @@ -899,7 +905,7 @@ public final class Locale implements Serializable, Cloneable */ public int hashCode() { - return hashcode; + return hashcodeCache; } /** @@ -917,32 +923,12 @@ public final class Locale implements Serializable, Cloneable return false; Locale l = (Locale) obj; - return (language == l.language - && country == l.country + return (language == l.language + && country == l.country && variant == l.variant); } /** - * Write the locale to an object stream. - * - * @param s the stream to write to - * @throws IOException if the write fails - * @serialData The first three fields are Strings representing language, - * country, and variant. The fourth field is a placeholder for - * the cached hashcode, but this is always written as -1, and - * recomputed when reading it back. - */ - private void writeObject(ObjectOutputStream s) - throws IOException - { - s.writeObject(language); - s.writeObject(country); - s.writeObject(variant); - // Hashcode field is always written as -1. - s.writeInt(-1); - } - - /** * Reads a locale from the input stream. * * @param s the stream to read from @@ -953,10 +939,10 @@ public final class Locale implements Serializable, Cloneable private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { - language = ((String) s.readObject()).intern(); - country = ((String) s.readObject()).intern(); - variant = ((String) s.readObject()).intern(); - // Recompute hashcode. - hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + s.defaultReadObject(); + language = language.intern(); + country = country.intern(); + variant = variant.intern(); + hashcodeCache = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); } } // class Locale diff --git a/java/util/ResourceBundle.java b/java/util/ResourceBundle.java index 751d380b2..9b82bc801 100644 --- a/java/util/ResourceBundle.java +++ b/java/util/ResourceBundle.java @@ -91,6 +91,14 @@ baseName</pre> public abstract class ResourceBundle { /** + * Maximum size of our cache of <code>ResourceBundle</code>s keyed by + * {@link BundleKey} instances. + * + * @see BundleKey + */ + private static final int CACHE_SIZE = 100; + + /** * The parent bundle. This is consulted when you call getObject and there * is no such resource in the current bundle. This field may be null. */ @@ -104,21 +112,22 @@ public abstract class ResourceBundle private Locale locale; /** - * The resource bundle cache. - */ - private static Map bundleCache; - - /** - * The last default Locale we saw. If this ever changes then we have to - * reset our caches. - */ - private static Locale lastDefaultLocale; - - /** - * The `empty' locale is created once in order to optimize - * tryBundle(). + * A VM-wide cache of resource bundles already fetched. + * <p> + * This {@link Map} is a Least Recently Used (LRU) cache, of the last + * {@link #CACHE_SIZE} accessed <code>ResourceBundle</code>s keyed by the + * tuple: default locale, resource-bundle name, resource-bundle locale, and + * classloader. + * + * @see BundleKey */ - private static final Locale emptyLocale = new Locale(""); + private static Map bundleCache = new LinkedHashMap(CACHE_SIZE + 1, 0.75F, true) + { + public boolean removeEldestEntry(Map.Entry entry) + { + return size() > CACHE_SIZE; + } + }; /** * The constructor. It does nothing special. @@ -246,6 +255,7 @@ public abstract class ResourceBundle by the combination of bundle name, locale, and class loader. */ private static class BundleKey { + Locale defaultLocale; String baseName; Locale locale; ClassLoader classLoader; @@ -253,18 +263,19 @@ public abstract class ResourceBundle BundleKey() {} - BundleKey(String s, Locale l, ClassLoader cl) + BundleKey(Locale dl, String s, Locale l, ClassLoader cl) { - set(s, l, cl); + set(dl, s, l, cl); } - void set(String s, Locale l, ClassLoader cl) + void set(Locale dl, String s, Locale l, ClassLoader cl) { + defaultLocale = dl; baseName = s; locale = l; classLoader = cl; - hashcode = baseName.hashCode() ^ locale.hashCode() ^ - classLoader.hashCode(); + hashcode = defaultLocale.hashCode() ^ baseName.hashCode() + ^ locale.hashCode() ^ classLoader.hashCode(); } public int hashCode() @@ -277,10 +288,11 @@ public abstract class ResourceBundle if (! (o instanceof BundleKey)) return false; BundleKey key = (BundleKey) o; - return hashcode == key.hashcode && - baseName.equals(key.baseName) && - locale.equals(key.locale) && - classLoader.equals(key.classLoader); + return hashcode == key.hashcode + && defaultLocale.equals(key.defaultLocale) + && baseName.equals(key.baseName) + && locale.equals(key.locale) + && classLoader.equals(key.classLoader); } } @@ -370,61 +382,39 @@ public abstract class ResourceBundle public static synchronized ResourceBundle getBundle (String baseName, Locale locale, ClassLoader classLoader) { - // If the default locale changed since the last time we were called, - // all cache entries are invalidated. Locale defaultLocale = Locale.getDefault(); - if (defaultLocale != lastDefaultLocale) - { - bundleCache = new HashMap(); - lastDefaultLocale = defaultLocale; - } - // This will throw NullPointerException if any arguments are null. - lookupKey.set(baseName, locale, classLoader); - + lookupKey.set(defaultLocale, baseName, locale, classLoader); Object obj = bundleCache.get(lookupKey); - ResourceBundle rb = null; - if (obj instanceof ResourceBundle) + return (ResourceBundle) obj; + + if (obj == nullEntry) + throw new MissingResourceException("Bundle " + baseName + + " not found for locale " + locale + + " by classloader " + classLoader, + baseName, ""); + // First, look for a bundle for the specified locale. We don't want + // the base bundle this time. + boolean wantBase = locale.equals(defaultLocale); + ResourceBundle bundle = tryBundle(baseName, locale, classLoader, wantBase); + // Try the default locale if neccessary. + if (bundle == null && ! wantBase) + bundle = tryBundle(baseName, defaultLocale, classLoader, true); + + BundleKey key = new BundleKey(defaultLocale, baseName, locale, classLoader); + if (bundle == null) { - return (ResourceBundle) obj; - } - else if (obj == nullEntry) - { - // Lookup has failed previously. Fall through. + // Cache the fact that this lookup has previously failed. + bundleCache.put(key, nullEntry); + throw new MissingResourceException("Bundle " + baseName + + " not found for locale " + locale + + " by classloader " + classLoader, + baseName, ""); } - else - { - // First, look for a bundle for the specified locale. We don't want - // the base bundle this time. - boolean wantBase = locale.equals(defaultLocale); - ResourceBundle bundle = tryBundle(baseName, locale, classLoader, - wantBase); - - // Try the default locale if neccessary. - if (bundle == null && !locale.equals(defaultLocale)) - bundle = tryBundle(baseName, defaultLocale, classLoader, true); - - BundleKey key = new BundleKey(baseName, locale, classLoader); - if (bundle == null) - { - // Cache the fact that this lookup has previously failed. - bundleCache.put(key, nullEntry); - } - else - { - // Cache the result and return it. - bundleCache.put(key, bundle); - return bundle; - } - } - - throw new MissingResourceException("Bundle " + baseName - + " not found for locale " - + locale - + " by classloader " - + classLoader, - baseName, ""); + // Cache the result and return it. + bundleCache.put(key, bundle); + return bundle; } /** diff --git a/java/util/Vector.java b/java/util/Vector.java index 02eb1539a..ea29ce093 100644 --- a/java/util/Vector.java +++ b/java/util/Vector.java @@ -720,8 +720,10 @@ public class Vector<T> extends AbstractList<T> */ public synchronized boolean removeAll(Collection<?> c) { - if (c == null) - throw new NullPointerException(); + // The NullPointerException is thrown implicitly when the Vector + // is not empty and c is null. The RI allows null arguments when + // the vector is empty. See Mauve test: + // gnu/testlet/java/util/Vector/removeAll.java int i; int j; @@ -749,8 +751,10 @@ public class Vector<T> extends AbstractList<T> */ public synchronized boolean retainAll(Collection<?> c) { - if (c == null) - throw new NullPointerException(); + // The NullPointerException is thrown implicitly when the Vector + // is not empty and c is null. The RI allows null arguments when + // the vector is empty. See Mauve test: + // gnu/testlet/java/util/Vector/retainAll.java int i; int j; diff --git a/java/util/regex/Matcher.java b/java/util/regex/Matcher.java index 25e73810e..3ddd42547 100644 --- a/java/util/regex/Matcher.java +++ b/java/util/regex/Matcher.java @@ -218,7 +218,7 @@ public final class Matcher implements MatchResult public boolean lookingAt () { - match = pattern.getRE().getMatch(inputCharIndexed, 0); + match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_FIX_STARTING_POSITION, null); if (match != null) { if (match.getStartIndex() == 0) @@ -243,7 +243,7 @@ public final class Matcher implements MatchResult */ public boolean matches () { - match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_TRY_ENTIRE_MATCH); + match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION, null); if (match != null) { if (match.getStartIndex() == 0) @@ -309,6 +309,28 @@ public final class Matcher implements MatchResult return match.getStartIndex(group); } + /** + * @return True if and only if the matcher hit the end of input. + */ + public boolean hitEnd() + { + return inputCharIndexed.hitEnd(); + } + + /** + * @return A string expression of this matcher. + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()) + .append("[pattern=").append(pattern.pattern()) + .append(" region=").append("0").append(",").append(input.length()) + .append(" lastmatch=").append(match == null ? "" : match.toString()) + .append("]"); + return sb.toString(); + } + private void assertMatchOp() { if (match == null) throw new IllegalStateException(); diff --git a/java/util/zip/ZipFile.java b/java/util/zip/ZipFile.java index 00ba19b15..3b34bd1f5 100644 --- a/java/util/zip/ZipFile.java +++ b/java/util/zip/ZipFile.java @@ -48,6 +48,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; @@ -516,6 +519,16 @@ public class ZipFile implements ZipConstants private static final class PartialInputStream extends InputStream { + /** + * The UTF-8 charset use for decoding the filenames. + */ + private static final Charset UTF8CHARSET = Charset.forName("UTF-8"); + + /** + * The actual UTF-8 decoder. Created on demand. + */ + private CharsetDecoder utf8Decoder; + private final RandomAccessFile raf; private final byte[] buffer; private long bufferOffset; @@ -652,23 +665,86 @@ public class ZipFile implements ZipConstants int readLeShort() throws IOException { - int b0 = read(); - int b1 = read(); - if (b1 == -1) - throw new EOFException(); - return (b0 & 0xff) | (b1 & 0xff) << 8; + int result; + if(pos + 1 < buffer.length) + { + result = ((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8); + pos += 2; + } + else + { + int b0 = read(); + int b1 = read(); + if (b1 == -1) + throw new EOFException(); + result = (b0 & 0xff) | (b1 & 0xff) << 8; + } + return result; } int readLeInt() throws IOException { - int b0 = read(); - int b1 = read(); - int b2 = read(); - int b3 = read(); - if (b3 == -1) - throw new EOFException(); - return ((b0 & 0xff) | (b1 & 0xff) << 8) - | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16; + int result; + if(pos + 3 < buffer.length) + { + result = (((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8) + | ((buffer[pos + 2] & 0xff) + | (buffer[pos + 3] & 0xff) << 8) << 16); + pos += 4; + } + else + { + int b0 = read(); + int b1 = read(); + int b2 = read(); + int b3 = read(); + if (b3 == -1) + throw new EOFException(); + result = (((b0 & 0xff) | (b1 & 0xff) << 8) | ((b2 & 0xff) + | (b3 & 0xff) << 8) << 16); + } + return result; + } + + /** + * Decode chars from byte buffer using UTF8 encoding. This + * operation is performance-critical since a jar file contains a + * large number of strings for the name of each file in the + * archive. This routine therefore avoids using the expensive + * utf8Decoder when decoding is straightforward. + * + * @param buffer the buffer that contains the encoded character + * data + * @param pos the index in buffer of the first byte of the encoded + * data + * @param length the length of the encoded data in number of + * bytes. + * + * @return a String that contains the decoded characters. + */ + private String decodeChars(byte[] buffer, int pos, int length) + throws IOException + { + String result; + int i=length - 1; + while ((i >= 0) && (buffer[i] <= 0x7f)) + { + i--; + } + if (i < 0) + { + result = new String(buffer, 0, pos, length); + } + else + { + ByteBuffer bufferBuffer = ByteBuffer.wrap(buffer, pos, length); + if (utf8Decoder == null) + utf8Decoder = UTF8CHARSET.newDecoder(); + utf8Decoder.reset(); + char [] characters = utf8Decoder.decode(bufferBuffer).array(); + result = String.valueOf(characters); + } + return result; } String readString(int length) throws IOException @@ -676,25 +752,26 @@ public class ZipFile implements ZipConstants if (length > end - (bufferOffset + pos)) throw new EOFException(); + String result = null; try { if (buffer.length - pos >= length) { - String s = new String(buffer, pos, length, "UTF-8"); + result = decodeChars(buffer, pos, length); pos += length; - return s; } else { byte[] b = new byte[length]; readFully(b); - return new String(b, 0, length, "UTF-8"); + result = decodeChars(b, 0, length); } } catch (UnsupportedEncodingException uee) { throw new AssertionError(uee); } + return result; } public void addDummyByte() |